1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * inftlcore.c -- Linux driver for Inverse Flash Translation Layer (INFTL)
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Copyright © 2002, Greg Ungerer (gerg@snapgear.com)
6*4882a593Smuzhiyun *
7*4882a593Smuzhiyun * Based heavily on the nftlcore.c code which is:
8*4882a593Smuzhiyun * Copyright © 1999 Machine Vision Holdings, Inc.
9*4882a593Smuzhiyun * Copyright © 1999 David Woodhouse <dwmw2@infradead.org>
10*4882a593Smuzhiyun */
11*4882a593Smuzhiyun
12*4882a593Smuzhiyun #include <linux/kernel.h>
13*4882a593Smuzhiyun #include <linux/module.h>
14*4882a593Smuzhiyun #include <linux/delay.h>
15*4882a593Smuzhiyun #include <linux/slab.h>
16*4882a593Smuzhiyun #include <linux/sched.h>
17*4882a593Smuzhiyun #include <linux/init.h>
18*4882a593Smuzhiyun #include <linux/kmod.h>
19*4882a593Smuzhiyun #include <linux/hdreg.h>
20*4882a593Smuzhiyun #include <linux/mtd/mtd.h>
21*4882a593Smuzhiyun #include <linux/mtd/nftl.h>
22*4882a593Smuzhiyun #include <linux/mtd/inftl.h>
23*4882a593Smuzhiyun #include <linux/mtd/rawnand.h>
24*4882a593Smuzhiyun #include <linux/uaccess.h>
25*4882a593Smuzhiyun #include <asm/errno.h>
26*4882a593Smuzhiyun #include <asm/io.h>
27*4882a593Smuzhiyun
28*4882a593Smuzhiyun /*
29*4882a593Smuzhiyun * Maximum number of loops while examining next block, to have a
30*4882a593Smuzhiyun * chance to detect consistency problems (they should never happen
31*4882a593Smuzhiyun * because of the checks done in the mounting.
32*4882a593Smuzhiyun */
33*4882a593Smuzhiyun #define MAX_LOOPS 10000
34*4882a593Smuzhiyun
inftl_add_mtd(struct mtd_blktrans_ops * tr,struct mtd_info * mtd)35*4882a593Smuzhiyun static void inftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
36*4882a593Smuzhiyun {
37*4882a593Smuzhiyun struct INFTLrecord *inftl;
38*4882a593Smuzhiyun unsigned long temp;
39*4882a593Smuzhiyun
40*4882a593Smuzhiyun if (!mtd_type_is_nand(mtd) || mtd->size > UINT_MAX)
41*4882a593Smuzhiyun return;
42*4882a593Smuzhiyun /* OK, this is moderately ugly. But probably safe. Alternatives? */
43*4882a593Smuzhiyun if (memcmp(mtd->name, "DiskOnChip", 10))
44*4882a593Smuzhiyun return;
45*4882a593Smuzhiyun
46*4882a593Smuzhiyun if (!mtd->_block_isbad) {
47*4882a593Smuzhiyun printk(KERN_ERR
48*4882a593Smuzhiyun "INFTL no longer supports the old DiskOnChip drivers loaded via docprobe.\n"
49*4882a593Smuzhiyun "Please use the new diskonchip driver under the NAND subsystem.\n");
50*4882a593Smuzhiyun return;
51*4882a593Smuzhiyun }
52*4882a593Smuzhiyun
53*4882a593Smuzhiyun pr_debug("INFTL: add_mtd for %s\n", mtd->name);
54*4882a593Smuzhiyun
55*4882a593Smuzhiyun inftl = kzalloc(sizeof(*inftl), GFP_KERNEL);
56*4882a593Smuzhiyun
57*4882a593Smuzhiyun if (!inftl)
58*4882a593Smuzhiyun return;
59*4882a593Smuzhiyun
60*4882a593Smuzhiyun inftl->mbd.mtd = mtd;
61*4882a593Smuzhiyun inftl->mbd.devnum = -1;
62*4882a593Smuzhiyun
63*4882a593Smuzhiyun inftl->mbd.tr = tr;
64*4882a593Smuzhiyun
65*4882a593Smuzhiyun if (INFTL_mount(inftl) < 0) {
66*4882a593Smuzhiyun printk(KERN_WARNING "INFTL: could not mount device\n");
67*4882a593Smuzhiyun kfree(inftl);
68*4882a593Smuzhiyun return;
69*4882a593Smuzhiyun }
70*4882a593Smuzhiyun
71*4882a593Smuzhiyun /* OK, it's a new one. Set up all the data structures. */
72*4882a593Smuzhiyun
73*4882a593Smuzhiyun /* Calculate geometry */
74*4882a593Smuzhiyun inftl->cylinders = 1024;
75*4882a593Smuzhiyun inftl->heads = 16;
76*4882a593Smuzhiyun
77*4882a593Smuzhiyun temp = inftl->cylinders * inftl->heads;
78*4882a593Smuzhiyun inftl->sectors = inftl->mbd.size / temp;
79*4882a593Smuzhiyun if (inftl->mbd.size % temp) {
80*4882a593Smuzhiyun inftl->sectors++;
81*4882a593Smuzhiyun temp = inftl->cylinders * inftl->sectors;
82*4882a593Smuzhiyun inftl->heads = inftl->mbd.size / temp;
83*4882a593Smuzhiyun
84*4882a593Smuzhiyun if (inftl->mbd.size % temp) {
85*4882a593Smuzhiyun inftl->heads++;
86*4882a593Smuzhiyun temp = inftl->heads * inftl->sectors;
87*4882a593Smuzhiyun inftl->cylinders = inftl->mbd.size / temp;
88*4882a593Smuzhiyun }
89*4882a593Smuzhiyun }
90*4882a593Smuzhiyun
91*4882a593Smuzhiyun if (inftl->mbd.size != inftl->heads * inftl->cylinders * inftl->sectors) {
92*4882a593Smuzhiyun /*
93*4882a593Smuzhiyun Oh no we don't have
94*4882a593Smuzhiyun mbd.size == heads * cylinders * sectors
95*4882a593Smuzhiyun */
96*4882a593Smuzhiyun printk(KERN_WARNING "INFTL: cannot calculate a geometry to "
97*4882a593Smuzhiyun "match size of 0x%lx.\n", inftl->mbd.size);
98*4882a593Smuzhiyun printk(KERN_WARNING "INFTL: using C:%d H:%d S:%d "
99*4882a593Smuzhiyun "(== 0x%lx sects)\n",
100*4882a593Smuzhiyun inftl->cylinders, inftl->heads , inftl->sectors,
101*4882a593Smuzhiyun (long)inftl->cylinders * (long)inftl->heads *
102*4882a593Smuzhiyun (long)inftl->sectors );
103*4882a593Smuzhiyun }
104*4882a593Smuzhiyun
105*4882a593Smuzhiyun if (add_mtd_blktrans_dev(&inftl->mbd)) {
106*4882a593Smuzhiyun kfree(inftl->PUtable);
107*4882a593Smuzhiyun kfree(inftl->VUtable);
108*4882a593Smuzhiyun kfree(inftl);
109*4882a593Smuzhiyun return;
110*4882a593Smuzhiyun }
111*4882a593Smuzhiyun #ifdef PSYCHO_DEBUG
112*4882a593Smuzhiyun printk(KERN_INFO "INFTL: Found new inftl%c\n", inftl->mbd.devnum + 'a');
113*4882a593Smuzhiyun #endif
114*4882a593Smuzhiyun return;
115*4882a593Smuzhiyun }
116*4882a593Smuzhiyun
inftl_remove_dev(struct mtd_blktrans_dev * dev)117*4882a593Smuzhiyun static void inftl_remove_dev(struct mtd_blktrans_dev *dev)
118*4882a593Smuzhiyun {
119*4882a593Smuzhiyun struct INFTLrecord *inftl = (void *)dev;
120*4882a593Smuzhiyun
121*4882a593Smuzhiyun pr_debug("INFTL: remove_dev (i=%d)\n", dev->devnum);
122*4882a593Smuzhiyun
123*4882a593Smuzhiyun del_mtd_blktrans_dev(dev);
124*4882a593Smuzhiyun
125*4882a593Smuzhiyun kfree(inftl->PUtable);
126*4882a593Smuzhiyun kfree(inftl->VUtable);
127*4882a593Smuzhiyun }
128*4882a593Smuzhiyun
129*4882a593Smuzhiyun /*
130*4882a593Smuzhiyun * Actual INFTL access routines.
131*4882a593Smuzhiyun */
132*4882a593Smuzhiyun
133*4882a593Smuzhiyun /*
134*4882a593Smuzhiyun * Read oob data from flash
135*4882a593Smuzhiyun */
inftl_read_oob(struct mtd_info * mtd,loff_t offs,size_t len,size_t * retlen,uint8_t * buf)136*4882a593Smuzhiyun int inftl_read_oob(struct mtd_info *mtd, loff_t offs, size_t len,
137*4882a593Smuzhiyun size_t *retlen, uint8_t *buf)
138*4882a593Smuzhiyun {
139*4882a593Smuzhiyun struct mtd_oob_ops ops;
140*4882a593Smuzhiyun int res;
141*4882a593Smuzhiyun
142*4882a593Smuzhiyun ops.mode = MTD_OPS_PLACE_OOB;
143*4882a593Smuzhiyun ops.ooboffs = offs & (mtd->writesize - 1);
144*4882a593Smuzhiyun ops.ooblen = len;
145*4882a593Smuzhiyun ops.oobbuf = buf;
146*4882a593Smuzhiyun ops.datbuf = NULL;
147*4882a593Smuzhiyun
148*4882a593Smuzhiyun res = mtd_read_oob(mtd, offs & ~(mtd->writesize - 1), &ops);
149*4882a593Smuzhiyun *retlen = ops.oobretlen;
150*4882a593Smuzhiyun return res;
151*4882a593Smuzhiyun }
152*4882a593Smuzhiyun
153*4882a593Smuzhiyun /*
154*4882a593Smuzhiyun * Write oob data to flash
155*4882a593Smuzhiyun */
inftl_write_oob(struct mtd_info * mtd,loff_t offs,size_t len,size_t * retlen,uint8_t * buf)156*4882a593Smuzhiyun int inftl_write_oob(struct mtd_info *mtd, loff_t offs, size_t len,
157*4882a593Smuzhiyun size_t *retlen, uint8_t *buf)
158*4882a593Smuzhiyun {
159*4882a593Smuzhiyun struct mtd_oob_ops ops;
160*4882a593Smuzhiyun int res;
161*4882a593Smuzhiyun
162*4882a593Smuzhiyun ops.mode = MTD_OPS_PLACE_OOB;
163*4882a593Smuzhiyun ops.ooboffs = offs & (mtd->writesize - 1);
164*4882a593Smuzhiyun ops.ooblen = len;
165*4882a593Smuzhiyun ops.oobbuf = buf;
166*4882a593Smuzhiyun ops.datbuf = NULL;
167*4882a593Smuzhiyun
168*4882a593Smuzhiyun res = mtd_write_oob(mtd, offs & ~(mtd->writesize - 1), &ops);
169*4882a593Smuzhiyun *retlen = ops.oobretlen;
170*4882a593Smuzhiyun return res;
171*4882a593Smuzhiyun }
172*4882a593Smuzhiyun
173*4882a593Smuzhiyun /*
174*4882a593Smuzhiyun * Write data and oob to flash
175*4882a593Smuzhiyun */
inftl_write(struct mtd_info * mtd,loff_t offs,size_t len,size_t * retlen,uint8_t * buf,uint8_t * oob)176*4882a593Smuzhiyun static int inftl_write(struct mtd_info *mtd, loff_t offs, size_t len,
177*4882a593Smuzhiyun size_t *retlen, uint8_t *buf, uint8_t *oob)
178*4882a593Smuzhiyun {
179*4882a593Smuzhiyun struct mtd_oob_ops ops;
180*4882a593Smuzhiyun int res;
181*4882a593Smuzhiyun
182*4882a593Smuzhiyun ops.mode = MTD_OPS_PLACE_OOB;
183*4882a593Smuzhiyun ops.ooboffs = offs;
184*4882a593Smuzhiyun ops.ooblen = mtd->oobsize;
185*4882a593Smuzhiyun ops.oobbuf = oob;
186*4882a593Smuzhiyun ops.datbuf = buf;
187*4882a593Smuzhiyun ops.len = len;
188*4882a593Smuzhiyun
189*4882a593Smuzhiyun res = mtd_write_oob(mtd, offs & ~(mtd->writesize - 1), &ops);
190*4882a593Smuzhiyun *retlen = ops.retlen;
191*4882a593Smuzhiyun return res;
192*4882a593Smuzhiyun }
193*4882a593Smuzhiyun
194*4882a593Smuzhiyun /*
195*4882a593Smuzhiyun * INFTL_findfreeblock: Find a free Erase Unit on the INFTL partition.
196*4882a593Smuzhiyun * This function is used when the give Virtual Unit Chain.
197*4882a593Smuzhiyun */
INFTL_findfreeblock(struct INFTLrecord * inftl,int desperate)198*4882a593Smuzhiyun static u16 INFTL_findfreeblock(struct INFTLrecord *inftl, int desperate)
199*4882a593Smuzhiyun {
200*4882a593Smuzhiyun u16 pot = inftl->LastFreeEUN;
201*4882a593Smuzhiyun int silly = inftl->nb_blocks;
202*4882a593Smuzhiyun
203*4882a593Smuzhiyun pr_debug("INFTL: INFTL_findfreeblock(inftl=%p,desperate=%d)\n",
204*4882a593Smuzhiyun inftl, desperate);
205*4882a593Smuzhiyun
206*4882a593Smuzhiyun /*
207*4882a593Smuzhiyun * Normally, we force a fold to happen before we run out of free
208*4882a593Smuzhiyun * blocks completely.
209*4882a593Smuzhiyun */
210*4882a593Smuzhiyun if (!desperate && inftl->numfreeEUNs < 2) {
211*4882a593Smuzhiyun pr_debug("INFTL: there are too few free EUNs (%d)\n",
212*4882a593Smuzhiyun inftl->numfreeEUNs);
213*4882a593Smuzhiyun return BLOCK_NIL;
214*4882a593Smuzhiyun }
215*4882a593Smuzhiyun
216*4882a593Smuzhiyun /* Scan for a free block */
217*4882a593Smuzhiyun do {
218*4882a593Smuzhiyun if (inftl->PUtable[pot] == BLOCK_FREE) {
219*4882a593Smuzhiyun inftl->LastFreeEUN = pot;
220*4882a593Smuzhiyun return pot;
221*4882a593Smuzhiyun }
222*4882a593Smuzhiyun
223*4882a593Smuzhiyun if (++pot > inftl->lastEUN)
224*4882a593Smuzhiyun pot = 0;
225*4882a593Smuzhiyun
226*4882a593Smuzhiyun if (!silly--) {
227*4882a593Smuzhiyun printk(KERN_WARNING "INFTL: no free blocks found! "
228*4882a593Smuzhiyun "EUN range = %d - %d\n", 0, inftl->LastFreeEUN);
229*4882a593Smuzhiyun return BLOCK_NIL;
230*4882a593Smuzhiyun }
231*4882a593Smuzhiyun } while (pot != inftl->LastFreeEUN);
232*4882a593Smuzhiyun
233*4882a593Smuzhiyun return BLOCK_NIL;
234*4882a593Smuzhiyun }
235*4882a593Smuzhiyun
INFTL_foldchain(struct INFTLrecord * inftl,unsigned thisVUC,unsigned pendingblock)236*4882a593Smuzhiyun static u16 INFTL_foldchain(struct INFTLrecord *inftl, unsigned thisVUC, unsigned pendingblock)
237*4882a593Smuzhiyun {
238*4882a593Smuzhiyun u16 BlockMap[MAX_SECTORS_PER_UNIT];
239*4882a593Smuzhiyun unsigned char BlockDeleted[MAX_SECTORS_PER_UNIT];
240*4882a593Smuzhiyun unsigned int thisEUN, prevEUN, status;
241*4882a593Smuzhiyun struct mtd_info *mtd = inftl->mbd.mtd;
242*4882a593Smuzhiyun int block, silly;
243*4882a593Smuzhiyun unsigned int targetEUN;
244*4882a593Smuzhiyun struct inftl_oob oob;
245*4882a593Smuzhiyun size_t retlen;
246*4882a593Smuzhiyun
247*4882a593Smuzhiyun pr_debug("INFTL: INFTL_foldchain(inftl=%p,thisVUC=%d,pending=%d)\n",
248*4882a593Smuzhiyun inftl, thisVUC, pendingblock);
249*4882a593Smuzhiyun
250*4882a593Smuzhiyun memset(BlockMap, 0xff, sizeof(BlockMap));
251*4882a593Smuzhiyun memset(BlockDeleted, 0, sizeof(BlockDeleted));
252*4882a593Smuzhiyun
253*4882a593Smuzhiyun thisEUN = targetEUN = inftl->VUtable[thisVUC];
254*4882a593Smuzhiyun
255*4882a593Smuzhiyun if (thisEUN == BLOCK_NIL) {
256*4882a593Smuzhiyun printk(KERN_WARNING "INFTL: trying to fold non-existent "
257*4882a593Smuzhiyun "Virtual Unit Chain %d!\n", thisVUC);
258*4882a593Smuzhiyun return BLOCK_NIL;
259*4882a593Smuzhiyun }
260*4882a593Smuzhiyun
261*4882a593Smuzhiyun /*
262*4882a593Smuzhiyun * Scan to find the Erase Unit which holds the actual data for each
263*4882a593Smuzhiyun * 512-byte block within the Chain.
264*4882a593Smuzhiyun */
265*4882a593Smuzhiyun silly = MAX_LOOPS;
266*4882a593Smuzhiyun while (thisEUN < inftl->nb_blocks) {
267*4882a593Smuzhiyun for (block = 0; block < inftl->EraseSize/SECTORSIZE; block ++) {
268*4882a593Smuzhiyun if ((BlockMap[block] != BLOCK_NIL) ||
269*4882a593Smuzhiyun BlockDeleted[block])
270*4882a593Smuzhiyun continue;
271*4882a593Smuzhiyun
272*4882a593Smuzhiyun if (inftl_read_oob(mtd, (thisEUN * inftl->EraseSize)
273*4882a593Smuzhiyun + (block * SECTORSIZE), 16, &retlen,
274*4882a593Smuzhiyun (char *)&oob) < 0)
275*4882a593Smuzhiyun status = SECTOR_IGNORE;
276*4882a593Smuzhiyun else
277*4882a593Smuzhiyun status = oob.b.Status | oob.b.Status1;
278*4882a593Smuzhiyun
279*4882a593Smuzhiyun switch(status) {
280*4882a593Smuzhiyun case SECTOR_FREE:
281*4882a593Smuzhiyun case SECTOR_IGNORE:
282*4882a593Smuzhiyun break;
283*4882a593Smuzhiyun case SECTOR_USED:
284*4882a593Smuzhiyun BlockMap[block] = thisEUN;
285*4882a593Smuzhiyun continue;
286*4882a593Smuzhiyun case SECTOR_DELETED:
287*4882a593Smuzhiyun BlockDeleted[block] = 1;
288*4882a593Smuzhiyun continue;
289*4882a593Smuzhiyun default:
290*4882a593Smuzhiyun printk(KERN_WARNING "INFTL: unknown status "
291*4882a593Smuzhiyun "for block %d in EUN %d: %x\n",
292*4882a593Smuzhiyun block, thisEUN, status);
293*4882a593Smuzhiyun break;
294*4882a593Smuzhiyun }
295*4882a593Smuzhiyun }
296*4882a593Smuzhiyun
297*4882a593Smuzhiyun if (!silly--) {
298*4882a593Smuzhiyun printk(KERN_WARNING "INFTL: infinite loop in Virtual "
299*4882a593Smuzhiyun "Unit Chain 0x%x\n", thisVUC);
300*4882a593Smuzhiyun return BLOCK_NIL;
301*4882a593Smuzhiyun }
302*4882a593Smuzhiyun
303*4882a593Smuzhiyun thisEUN = inftl->PUtable[thisEUN];
304*4882a593Smuzhiyun }
305*4882a593Smuzhiyun
306*4882a593Smuzhiyun /*
307*4882a593Smuzhiyun * OK. We now know the location of every block in the Virtual Unit
308*4882a593Smuzhiyun * Chain, and the Erase Unit into which we are supposed to be copying.
309*4882a593Smuzhiyun * Go for it.
310*4882a593Smuzhiyun */
311*4882a593Smuzhiyun pr_debug("INFTL: folding chain %d into unit %d\n", thisVUC, targetEUN);
312*4882a593Smuzhiyun
313*4882a593Smuzhiyun for (block = 0; block < inftl->EraseSize/SECTORSIZE ; block++) {
314*4882a593Smuzhiyun unsigned char movebuf[SECTORSIZE];
315*4882a593Smuzhiyun int ret;
316*4882a593Smuzhiyun
317*4882a593Smuzhiyun /*
318*4882a593Smuzhiyun * If it's in the target EUN already, or if it's pending write,
319*4882a593Smuzhiyun * do nothing.
320*4882a593Smuzhiyun */
321*4882a593Smuzhiyun if (BlockMap[block] == targetEUN || (pendingblock ==
322*4882a593Smuzhiyun (thisVUC * (inftl->EraseSize / SECTORSIZE) + block))) {
323*4882a593Smuzhiyun continue;
324*4882a593Smuzhiyun }
325*4882a593Smuzhiyun
326*4882a593Smuzhiyun /*
327*4882a593Smuzhiyun * Copy only in non free block (free blocks can only
328*4882a593Smuzhiyun * happen in case of media errors or deleted blocks).
329*4882a593Smuzhiyun */
330*4882a593Smuzhiyun if (BlockMap[block] == BLOCK_NIL)
331*4882a593Smuzhiyun continue;
332*4882a593Smuzhiyun
333*4882a593Smuzhiyun ret = mtd_read(mtd,
334*4882a593Smuzhiyun (inftl->EraseSize * BlockMap[block]) + (block * SECTORSIZE),
335*4882a593Smuzhiyun SECTORSIZE,
336*4882a593Smuzhiyun &retlen,
337*4882a593Smuzhiyun movebuf);
338*4882a593Smuzhiyun if (ret < 0 && !mtd_is_bitflip(ret)) {
339*4882a593Smuzhiyun ret = mtd_read(mtd,
340*4882a593Smuzhiyun (inftl->EraseSize * BlockMap[block]) + (block * SECTORSIZE),
341*4882a593Smuzhiyun SECTORSIZE,
342*4882a593Smuzhiyun &retlen,
343*4882a593Smuzhiyun movebuf);
344*4882a593Smuzhiyun if (ret != -EIO)
345*4882a593Smuzhiyun pr_debug("INFTL: error went away on retry?\n");
346*4882a593Smuzhiyun }
347*4882a593Smuzhiyun memset(&oob, 0xff, sizeof(struct inftl_oob));
348*4882a593Smuzhiyun oob.b.Status = oob.b.Status1 = SECTOR_USED;
349*4882a593Smuzhiyun
350*4882a593Smuzhiyun inftl_write(inftl->mbd.mtd, (inftl->EraseSize * targetEUN) +
351*4882a593Smuzhiyun (block * SECTORSIZE), SECTORSIZE, &retlen,
352*4882a593Smuzhiyun movebuf, (char *)&oob);
353*4882a593Smuzhiyun }
354*4882a593Smuzhiyun
355*4882a593Smuzhiyun /*
356*4882a593Smuzhiyun * Newest unit in chain now contains data from _all_ older units.
357*4882a593Smuzhiyun * So go through and erase each unit in chain, oldest first. (This
358*4882a593Smuzhiyun * is important, by doing oldest first if we crash/reboot then it
359*4882a593Smuzhiyun * it is relatively simple to clean up the mess).
360*4882a593Smuzhiyun */
361*4882a593Smuzhiyun pr_debug("INFTL: want to erase virtual chain %d\n", thisVUC);
362*4882a593Smuzhiyun
363*4882a593Smuzhiyun for (;;) {
364*4882a593Smuzhiyun /* Find oldest unit in chain. */
365*4882a593Smuzhiyun thisEUN = inftl->VUtable[thisVUC];
366*4882a593Smuzhiyun prevEUN = BLOCK_NIL;
367*4882a593Smuzhiyun while (inftl->PUtable[thisEUN] != BLOCK_NIL) {
368*4882a593Smuzhiyun prevEUN = thisEUN;
369*4882a593Smuzhiyun thisEUN = inftl->PUtable[thisEUN];
370*4882a593Smuzhiyun }
371*4882a593Smuzhiyun
372*4882a593Smuzhiyun /* Check if we are all done */
373*4882a593Smuzhiyun if (thisEUN == targetEUN)
374*4882a593Smuzhiyun break;
375*4882a593Smuzhiyun
376*4882a593Smuzhiyun /* Unlink the last block from the chain. */
377*4882a593Smuzhiyun inftl->PUtable[prevEUN] = BLOCK_NIL;
378*4882a593Smuzhiyun
379*4882a593Smuzhiyun /* Now try to erase it. */
380*4882a593Smuzhiyun if (INFTL_formatblock(inftl, thisEUN) < 0) {
381*4882a593Smuzhiyun /*
382*4882a593Smuzhiyun * Could not erase : mark block as reserved.
383*4882a593Smuzhiyun */
384*4882a593Smuzhiyun inftl->PUtable[thisEUN] = BLOCK_RESERVED;
385*4882a593Smuzhiyun } else {
386*4882a593Smuzhiyun /* Correctly erased : mark it as free */
387*4882a593Smuzhiyun inftl->PUtable[thisEUN] = BLOCK_FREE;
388*4882a593Smuzhiyun inftl->numfreeEUNs++;
389*4882a593Smuzhiyun }
390*4882a593Smuzhiyun }
391*4882a593Smuzhiyun
392*4882a593Smuzhiyun return targetEUN;
393*4882a593Smuzhiyun }
394*4882a593Smuzhiyun
INFTL_makefreeblock(struct INFTLrecord * inftl,unsigned pendingblock)395*4882a593Smuzhiyun static u16 INFTL_makefreeblock(struct INFTLrecord *inftl, unsigned pendingblock)
396*4882a593Smuzhiyun {
397*4882a593Smuzhiyun /*
398*4882a593Smuzhiyun * This is the part that needs some cleverness applied.
399*4882a593Smuzhiyun * For now, I'm doing the minimum applicable to actually
400*4882a593Smuzhiyun * get the thing to work.
401*4882a593Smuzhiyun * Wear-levelling and other clever stuff needs to be implemented
402*4882a593Smuzhiyun * and we also need to do some assessment of the results when
403*4882a593Smuzhiyun * the system loses power half-way through the routine.
404*4882a593Smuzhiyun */
405*4882a593Smuzhiyun u16 LongestChain = 0;
406*4882a593Smuzhiyun u16 ChainLength = 0, thislen;
407*4882a593Smuzhiyun u16 chain, EUN;
408*4882a593Smuzhiyun
409*4882a593Smuzhiyun pr_debug("INFTL: INFTL_makefreeblock(inftl=%p,"
410*4882a593Smuzhiyun "pending=%d)\n", inftl, pendingblock);
411*4882a593Smuzhiyun
412*4882a593Smuzhiyun for (chain = 0; chain < inftl->nb_blocks; chain++) {
413*4882a593Smuzhiyun EUN = inftl->VUtable[chain];
414*4882a593Smuzhiyun thislen = 0;
415*4882a593Smuzhiyun
416*4882a593Smuzhiyun while (EUN <= inftl->lastEUN) {
417*4882a593Smuzhiyun thislen++;
418*4882a593Smuzhiyun EUN = inftl->PUtable[EUN];
419*4882a593Smuzhiyun if (thislen > 0xff00) {
420*4882a593Smuzhiyun printk(KERN_WARNING "INFTL: endless loop in "
421*4882a593Smuzhiyun "Virtual Chain %d: Unit %x\n",
422*4882a593Smuzhiyun chain, EUN);
423*4882a593Smuzhiyun /*
424*4882a593Smuzhiyun * Actually, don't return failure.
425*4882a593Smuzhiyun * Just ignore this chain and get on with it.
426*4882a593Smuzhiyun */
427*4882a593Smuzhiyun thislen = 0;
428*4882a593Smuzhiyun break;
429*4882a593Smuzhiyun }
430*4882a593Smuzhiyun }
431*4882a593Smuzhiyun
432*4882a593Smuzhiyun if (thislen > ChainLength) {
433*4882a593Smuzhiyun ChainLength = thislen;
434*4882a593Smuzhiyun LongestChain = chain;
435*4882a593Smuzhiyun }
436*4882a593Smuzhiyun }
437*4882a593Smuzhiyun
438*4882a593Smuzhiyun if (ChainLength < 2) {
439*4882a593Smuzhiyun printk(KERN_WARNING "INFTL: no Virtual Unit Chains available "
440*4882a593Smuzhiyun "for folding. Failing request\n");
441*4882a593Smuzhiyun return BLOCK_NIL;
442*4882a593Smuzhiyun }
443*4882a593Smuzhiyun
444*4882a593Smuzhiyun return INFTL_foldchain(inftl, LongestChain, pendingblock);
445*4882a593Smuzhiyun }
446*4882a593Smuzhiyun
nrbits(unsigned int val,int bitcount)447*4882a593Smuzhiyun static int nrbits(unsigned int val, int bitcount)
448*4882a593Smuzhiyun {
449*4882a593Smuzhiyun int i, total = 0;
450*4882a593Smuzhiyun
451*4882a593Smuzhiyun for (i = 0; (i < bitcount); i++)
452*4882a593Smuzhiyun total += (((0x1 << i) & val) ? 1 : 0);
453*4882a593Smuzhiyun return total;
454*4882a593Smuzhiyun }
455*4882a593Smuzhiyun
456*4882a593Smuzhiyun /*
457*4882a593Smuzhiyun * INFTL_findwriteunit: Return the unit number into which we can write
458*4882a593Smuzhiyun * for this block. Make it available if it isn't already.
459*4882a593Smuzhiyun */
INFTL_findwriteunit(struct INFTLrecord * inftl,unsigned block)460*4882a593Smuzhiyun static inline u16 INFTL_findwriteunit(struct INFTLrecord *inftl, unsigned block)
461*4882a593Smuzhiyun {
462*4882a593Smuzhiyun unsigned int thisVUC = block / (inftl->EraseSize / SECTORSIZE);
463*4882a593Smuzhiyun unsigned int thisEUN, writeEUN, prev_block, status;
464*4882a593Smuzhiyun unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize -1);
465*4882a593Smuzhiyun struct mtd_info *mtd = inftl->mbd.mtd;
466*4882a593Smuzhiyun struct inftl_oob oob;
467*4882a593Smuzhiyun struct inftl_bci bci;
468*4882a593Smuzhiyun unsigned char anac, nacs, parity;
469*4882a593Smuzhiyun size_t retlen;
470*4882a593Smuzhiyun int silly, silly2 = 3;
471*4882a593Smuzhiyun
472*4882a593Smuzhiyun pr_debug("INFTL: INFTL_findwriteunit(inftl=%p,block=%d)\n",
473*4882a593Smuzhiyun inftl, block);
474*4882a593Smuzhiyun
475*4882a593Smuzhiyun do {
476*4882a593Smuzhiyun /*
477*4882a593Smuzhiyun * Scan the media to find a unit in the VUC which has
478*4882a593Smuzhiyun * a free space for the block in question.
479*4882a593Smuzhiyun */
480*4882a593Smuzhiyun writeEUN = BLOCK_NIL;
481*4882a593Smuzhiyun thisEUN = inftl->VUtable[thisVUC];
482*4882a593Smuzhiyun silly = MAX_LOOPS;
483*4882a593Smuzhiyun
484*4882a593Smuzhiyun while (thisEUN <= inftl->lastEUN) {
485*4882a593Smuzhiyun inftl_read_oob(mtd, (thisEUN * inftl->EraseSize) +
486*4882a593Smuzhiyun blockofs, 8, &retlen, (char *)&bci);
487*4882a593Smuzhiyun
488*4882a593Smuzhiyun status = bci.Status | bci.Status1;
489*4882a593Smuzhiyun pr_debug("INFTL: status of block %d in EUN %d is %x\n",
490*4882a593Smuzhiyun block , writeEUN, status);
491*4882a593Smuzhiyun
492*4882a593Smuzhiyun switch(status) {
493*4882a593Smuzhiyun case SECTOR_FREE:
494*4882a593Smuzhiyun writeEUN = thisEUN;
495*4882a593Smuzhiyun break;
496*4882a593Smuzhiyun case SECTOR_DELETED:
497*4882a593Smuzhiyun case SECTOR_USED:
498*4882a593Smuzhiyun /* Can't go any further */
499*4882a593Smuzhiyun goto hitused;
500*4882a593Smuzhiyun case SECTOR_IGNORE:
501*4882a593Smuzhiyun break;
502*4882a593Smuzhiyun default:
503*4882a593Smuzhiyun /*
504*4882a593Smuzhiyun * Invalid block. Don't use it any more.
505*4882a593Smuzhiyun * Must implement.
506*4882a593Smuzhiyun */
507*4882a593Smuzhiyun break;
508*4882a593Smuzhiyun }
509*4882a593Smuzhiyun
510*4882a593Smuzhiyun if (!silly--) {
511*4882a593Smuzhiyun printk(KERN_WARNING "INFTL: infinite loop in "
512*4882a593Smuzhiyun "Virtual Unit Chain 0x%x\n", thisVUC);
513*4882a593Smuzhiyun return BLOCK_NIL;
514*4882a593Smuzhiyun }
515*4882a593Smuzhiyun
516*4882a593Smuzhiyun /* Skip to next block in chain */
517*4882a593Smuzhiyun thisEUN = inftl->PUtable[thisEUN];
518*4882a593Smuzhiyun }
519*4882a593Smuzhiyun
520*4882a593Smuzhiyun hitused:
521*4882a593Smuzhiyun if (writeEUN != BLOCK_NIL)
522*4882a593Smuzhiyun return writeEUN;
523*4882a593Smuzhiyun
524*4882a593Smuzhiyun
525*4882a593Smuzhiyun /*
526*4882a593Smuzhiyun * OK. We didn't find one in the existing chain, or there
527*4882a593Smuzhiyun * is no existing chain. Allocate a new one.
528*4882a593Smuzhiyun */
529*4882a593Smuzhiyun writeEUN = INFTL_findfreeblock(inftl, 0);
530*4882a593Smuzhiyun
531*4882a593Smuzhiyun if (writeEUN == BLOCK_NIL) {
532*4882a593Smuzhiyun /*
533*4882a593Smuzhiyun * That didn't work - there were no free blocks just
534*4882a593Smuzhiyun * waiting to be picked up. We're going to have to fold
535*4882a593Smuzhiyun * a chain to make room.
536*4882a593Smuzhiyun */
537*4882a593Smuzhiyun thisEUN = INFTL_makefreeblock(inftl, block);
538*4882a593Smuzhiyun
539*4882a593Smuzhiyun /*
540*4882a593Smuzhiyun * Hopefully we free something, lets try again.
541*4882a593Smuzhiyun * This time we are desperate...
542*4882a593Smuzhiyun */
543*4882a593Smuzhiyun pr_debug("INFTL: using desperate==1 to find free EUN "
544*4882a593Smuzhiyun "to accommodate write to VUC %d\n",
545*4882a593Smuzhiyun thisVUC);
546*4882a593Smuzhiyun writeEUN = INFTL_findfreeblock(inftl, 1);
547*4882a593Smuzhiyun if (writeEUN == BLOCK_NIL) {
548*4882a593Smuzhiyun /*
549*4882a593Smuzhiyun * Ouch. This should never happen - we should
550*4882a593Smuzhiyun * always be able to make some room somehow.
551*4882a593Smuzhiyun * If we get here, we've allocated more storage
552*4882a593Smuzhiyun * space than actual media, or our makefreeblock
553*4882a593Smuzhiyun * routine is missing something.
554*4882a593Smuzhiyun */
555*4882a593Smuzhiyun printk(KERN_WARNING "INFTL: cannot make free "
556*4882a593Smuzhiyun "space.\n");
557*4882a593Smuzhiyun #ifdef DEBUG
558*4882a593Smuzhiyun INFTL_dumptables(inftl);
559*4882a593Smuzhiyun INFTL_dumpVUchains(inftl);
560*4882a593Smuzhiyun #endif
561*4882a593Smuzhiyun return BLOCK_NIL;
562*4882a593Smuzhiyun }
563*4882a593Smuzhiyun }
564*4882a593Smuzhiyun
565*4882a593Smuzhiyun /*
566*4882a593Smuzhiyun * Insert new block into virtual chain. Firstly update the
567*4882a593Smuzhiyun * block headers in flash...
568*4882a593Smuzhiyun */
569*4882a593Smuzhiyun anac = 0;
570*4882a593Smuzhiyun nacs = 0;
571*4882a593Smuzhiyun thisEUN = inftl->VUtable[thisVUC];
572*4882a593Smuzhiyun if (thisEUN != BLOCK_NIL) {
573*4882a593Smuzhiyun inftl_read_oob(mtd, thisEUN * inftl->EraseSize
574*4882a593Smuzhiyun + 8, 8, &retlen, (char *)&oob.u);
575*4882a593Smuzhiyun anac = oob.u.a.ANAC + 1;
576*4882a593Smuzhiyun nacs = oob.u.a.NACs + 1;
577*4882a593Smuzhiyun }
578*4882a593Smuzhiyun
579*4882a593Smuzhiyun prev_block = inftl->VUtable[thisVUC];
580*4882a593Smuzhiyun if (prev_block < inftl->nb_blocks)
581*4882a593Smuzhiyun prev_block -= inftl->firstEUN;
582*4882a593Smuzhiyun
583*4882a593Smuzhiyun parity = (nrbits(thisVUC, 16) & 0x1) ? 0x1 : 0;
584*4882a593Smuzhiyun parity |= (nrbits(prev_block, 16) & 0x1) ? 0x2 : 0;
585*4882a593Smuzhiyun parity |= (nrbits(anac, 8) & 0x1) ? 0x4 : 0;
586*4882a593Smuzhiyun parity |= (nrbits(nacs, 8) & 0x1) ? 0x8 : 0;
587*4882a593Smuzhiyun
588*4882a593Smuzhiyun oob.u.a.virtualUnitNo = cpu_to_le16(thisVUC);
589*4882a593Smuzhiyun oob.u.a.prevUnitNo = cpu_to_le16(prev_block);
590*4882a593Smuzhiyun oob.u.a.ANAC = anac;
591*4882a593Smuzhiyun oob.u.a.NACs = nacs;
592*4882a593Smuzhiyun oob.u.a.parityPerField = parity;
593*4882a593Smuzhiyun oob.u.a.discarded = 0xaa;
594*4882a593Smuzhiyun
595*4882a593Smuzhiyun inftl_write_oob(mtd, writeEUN * inftl->EraseSize + 8, 8,
596*4882a593Smuzhiyun &retlen, (char *)&oob.u);
597*4882a593Smuzhiyun
598*4882a593Smuzhiyun /* Also back up header... */
599*4882a593Smuzhiyun oob.u.b.virtualUnitNo = cpu_to_le16(thisVUC);
600*4882a593Smuzhiyun oob.u.b.prevUnitNo = cpu_to_le16(prev_block);
601*4882a593Smuzhiyun oob.u.b.ANAC = anac;
602*4882a593Smuzhiyun oob.u.b.NACs = nacs;
603*4882a593Smuzhiyun oob.u.b.parityPerField = parity;
604*4882a593Smuzhiyun oob.u.b.discarded = 0xaa;
605*4882a593Smuzhiyun
606*4882a593Smuzhiyun inftl_write_oob(mtd, writeEUN * inftl->EraseSize +
607*4882a593Smuzhiyun SECTORSIZE * 4 + 8, 8, &retlen, (char *)&oob.u);
608*4882a593Smuzhiyun
609*4882a593Smuzhiyun inftl->PUtable[writeEUN] = inftl->VUtable[thisVUC];
610*4882a593Smuzhiyun inftl->VUtable[thisVUC] = writeEUN;
611*4882a593Smuzhiyun
612*4882a593Smuzhiyun inftl->numfreeEUNs--;
613*4882a593Smuzhiyun return writeEUN;
614*4882a593Smuzhiyun
615*4882a593Smuzhiyun } while (silly2--);
616*4882a593Smuzhiyun
617*4882a593Smuzhiyun printk(KERN_WARNING "INFTL: error folding to make room for Virtual "
618*4882a593Smuzhiyun "Unit Chain 0x%x\n", thisVUC);
619*4882a593Smuzhiyun return BLOCK_NIL;
620*4882a593Smuzhiyun }
621*4882a593Smuzhiyun
622*4882a593Smuzhiyun /*
623*4882a593Smuzhiyun * Given a Virtual Unit Chain, see if it can be deleted, and if so do it.
624*4882a593Smuzhiyun */
INFTL_trydeletechain(struct INFTLrecord * inftl,unsigned thisVUC)625*4882a593Smuzhiyun static void INFTL_trydeletechain(struct INFTLrecord *inftl, unsigned thisVUC)
626*4882a593Smuzhiyun {
627*4882a593Smuzhiyun struct mtd_info *mtd = inftl->mbd.mtd;
628*4882a593Smuzhiyun unsigned char BlockUsed[MAX_SECTORS_PER_UNIT];
629*4882a593Smuzhiyun unsigned char BlockDeleted[MAX_SECTORS_PER_UNIT];
630*4882a593Smuzhiyun unsigned int thisEUN, status;
631*4882a593Smuzhiyun int block, silly;
632*4882a593Smuzhiyun struct inftl_bci bci;
633*4882a593Smuzhiyun size_t retlen;
634*4882a593Smuzhiyun
635*4882a593Smuzhiyun pr_debug("INFTL: INFTL_trydeletechain(inftl=%p,"
636*4882a593Smuzhiyun "thisVUC=%d)\n", inftl, thisVUC);
637*4882a593Smuzhiyun
638*4882a593Smuzhiyun memset(BlockUsed, 0, sizeof(BlockUsed));
639*4882a593Smuzhiyun memset(BlockDeleted, 0, sizeof(BlockDeleted));
640*4882a593Smuzhiyun
641*4882a593Smuzhiyun thisEUN = inftl->VUtable[thisVUC];
642*4882a593Smuzhiyun if (thisEUN == BLOCK_NIL) {
643*4882a593Smuzhiyun printk(KERN_WARNING "INFTL: trying to delete non-existent "
644*4882a593Smuzhiyun "Virtual Unit Chain %d!\n", thisVUC);
645*4882a593Smuzhiyun return;
646*4882a593Smuzhiyun }
647*4882a593Smuzhiyun
648*4882a593Smuzhiyun /*
649*4882a593Smuzhiyun * Scan through the Erase Units to determine whether any data is in
650*4882a593Smuzhiyun * each of the 512-byte blocks within the Chain.
651*4882a593Smuzhiyun */
652*4882a593Smuzhiyun silly = MAX_LOOPS;
653*4882a593Smuzhiyun while (thisEUN < inftl->nb_blocks) {
654*4882a593Smuzhiyun for (block = 0; block < inftl->EraseSize/SECTORSIZE; block++) {
655*4882a593Smuzhiyun if (BlockUsed[block] || BlockDeleted[block])
656*4882a593Smuzhiyun continue;
657*4882a593Smuzhiyun
658*4882a593Smuzhiyun if (inftl_read_oob(mtd, (thisEUN * inftl->EraseSize)
659*4882a593Smuzhiyun + (block * SECTORSIZE), 8 , &retlen,
660*4882a593Smuzhiyun (char *)&bci) < 0)
661*4882a593Smuzhiyun status = SECTOR_IGNORE;
662*4882a593Smuzhiyun else
663*4882a593Smuzhiyun status = bci.Status | bci.Status1;
664*4882a593Smuzhiyun
665*4882a593Smuzhiyun switch(status) {
666*4882a593Smuzhiyun case SECTOR_FREE:
667*4882a593Smuzhiyun case SECTOR_IGNORE:
668*4882a593Smuzhiyun break;
669*4882a593Smuzhiyun case SECTOR_USED:
670*4882a593Smuzhiyun BlockUsed[block] = 1;
671*4882a593Smuzhiyun continue;
672*4882a593Smuzhiyun case SECTOR_DELETED:
673*4882a593Smuzhiyun BlockDeleted[block] = 1;
674*4882a593Smuzhiyun continue;
675*4882a593Smuzhiyun default:
676*4882a593Smuzhiyun printk(KERN_WARNING "INFTL: unknown status "
677*4882a593Smuzhiyun "for block %d in EUN %d: 0x%x\n",
678*4882a593Smuzhiyun block, thisEUN, status);
679*4882a593Smuzhiyun }
680*4882a593Smuzhiyun }
681*4882a593Smuzhiyun
682*4882a593Smuzhiyun if (!silly--) {
683*4882a593Smuzhiyun printk(KERN_WARNING "INFTL: infinite loop in Virtual "
684*4882a593Smuzhiyun "Unit Chain 0x%x\n", thisVUC);
685*4882a593Smuzhiyun return;
686*4882a593Smuzhiyun }
687*4882a593Smuzhiyun
688*4882a593Smuzhiyun thisEUN = inftl->PUtable[thisEUN];
689*4882a593Smuzhiyun }
690*4882a593Smuzhiyun
691*4882a593Smuzhiyun for (block = 0; block < inftl->EraseSize/SECTORSIZE; block++)
692*4882a593Smuzhiyun if (BlockUsed[block])
693*4882a593Smuzhiyun return;
694*4882a593Smuzhiyun
695*4882a593Smuzhiyun /*
696*4882a593Smuzhiyun * For each block in the chain free it and make it available
697*4882a593Smuzhiyun * for future use. Erase from the oldest unit first.
698*4882a593Smuzhiyun */
699*4882a593Smuzhiyun pr_debug("INFTL: deleting empty VUC %d\n", thisVUC);
700*4882a593Smuzhiyun
701*4882a593Smuzhiyun for (;;) {
702*4882a593Smuzhiyun u16 *prevEUN = &inftl->VUtable[thisVUC];
703*4882a593Smuzhiyun thisEUN = *prevEUN;
704*4882a593Smuzhiyun
705*4882a593Smuzhiyun /* If the chain is all gone already, we're done */
706*4882a593Smuzhiyun if (thisEUN == BLOCK_NIL) {
707*4882a593Smuzhiyun pr_debug("INFTL: Empty VUC %d for deletion was already absent\n", thisEUN);
708*4882a593Smuzhiyun return;
709*4882a593Smuzhiyun }
710*4882a593Smuzhiyun
711*4882a593Smuzhiyun /* Find oldest unit in chain. */
712*4882a593Smuzhiyun while (inftl->PUtable[thisEUN] != BLOCK_NIL) {
713*4882a593Smuzhiyun BUG_ON(thisEUN >= inftl->nb_blocks);
714*4882a593Smuzhiyun
715*4882a593Smuzhiyun prevEUN = &inftl->PUtable[thisEUN];
716*4882a593Smuzhiyun thisEUN = *prevEUN;
717*4882a593Smuzhiyun }
718*4882a593Smuzhiyun
719*4882a593Smuzhiyun pr_debug("Deleting EUN %d from VUC %d\n",
720*4882a593Smuzhiyun thisEUN, thisVUC);
721*4882a593Smuzhiyun
722*4882a593Smuzhiyun if (INFTL_formatblock(inftl, thisEUN) < 0) {
723*4882a593Smuzhiyun /*
724*4882a593Smuzhiyun * Could not erase : mark block as reserved.
725*4882a593Smuzhiyun */
726*4882a593Smuzhiyun inftl->PUtable[thisEUN] = BLOCK_RESERVED;
727*4882a593Smuzhiyun } else {
728*4882a593Smuzhiyun /* Correctly erased : mark it as free */
729*4882a593Smuzhiyun inftl->PUtable[thisEUN] = BLOCK_FREE;
730*4882a593Smuzhiyun inftl->numfreeEUNs++;
731*4882a593Smuzhiyun }
732*4882a593Smuzhiyun
733*4882a593Smuzhiyun /* Now sort out whatever was pointing to it... */
734*4882a593Smuzhiyun *prevEUN = BLOCK_NIL;
735*4882a593Smuzhiyun
736*4882a593Smuzhiyun /* Ideally we'd actually be responsive to new
737*4882a593Smuzhiyun requests while we're doing this -- if there's
738*4882a593Smuzhiyun free space why should others be made to wait? */
739*4882a593Smuzhiyun cond_resched();
740*4882a593Smuzhiyun }
741*4882a593Smuzhiyun
742*4882a593Smuzhiyun inftl->VUtable[thisVUC] = BLOCK_NIL;
743*4882a593Smuzhiyun }
744*4882a593Smuzhiyun
INFTL_deleteblock(struct INFTLrecord * inftl,unsigned block)745*4882a593Smuzhiyun static int INFTL_deleteblock(struct INFTLrecord *inftl, unsigned block)
746*4882a593Smuzhiyun {
747*4882a593Smuzhiyun unsigned int thisEUN = inftl->VUtable[block / (inftl->EraseSize / SECTORSIZE)];
748*4882a593Smuzhiyun unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize - 1);
749*4882a593Smuzhiyun struct mtd_info *mtd = inftl->mbd.mtd;
750*4882a593Smuzhiyun unsigned int status;
751*4882a593Smuzhiyun int silly = MAX_LOOPS;
752*4882a593Smuzhiyun size_t retlen;
753*4882a593Smuzhiyun struct inftl_bci bci;
754*4882a593Smuzhiyun
755*4882a593Smuzhiyun pr_debug("INFTL: INFTL_deleteblock(inftl=%p,"
756*4882a593Smuzhiyun "block=%d)\n", inftl, block);
757*4882a593Smuzhiyun
758*4882a593Smuzhiyun while (thisEUN < inftl->nb_blocks) {
759*4882a593Smuzhiyun if (inftl_read_oob(mtd, (thisEUN * inftl->EraseSize) +
760*4882a593Smuzhiyun blockofs, 8, &retlen, (char *)&bci) < 0)
761*4882a593Smuzhiyun status = SECTOR_IGNORE;
762*4882a593Smuzhiyun else
763*4882a593Smuzhiyun status = bci.Status | bci.Status1;
764*4882a593Smuzhiyun
765*4882a593Smuzhiyun switch (status) {
766*4882a593Smuzhiyun case SECTOR_FREE:
767*4882a593Smuzhiyun case SECTOR_IGNORE:
768*4882a593Smuzhiyun break;
769*4882a593Smuzhiyun case SECTOR_DELETED:
770*4882a593Smuzhiyun thisEUN = BLOCK_NIL;
771*4882a593Smuzhiyun goto foundit;
772*4882a593Smuzhiyun case SECTOR_USED:
773*4882a593Smuzhiyun goto foundit;
774*4882a593Smuzhiyun default:
775*4882a593Smuzhiyun printk(KERN_WARNING "INFTL: unknown status for "
776*4882a593Smuzhiyun "block %d in EUN %d: 0x%x\n",
777*4882a593Smuzhiyun block, thisEUN, status);
778*4882a593Smuzhiyun break;
779*4882a593Smuzhiyun }
780*4882a593Smuzhiyun
781*4882a593Smuzhiyun if (!silly--) {
782*4882a593Smuzhiyun printk(KERN_WARNING "INFTL: infinite loop in Virtual "
783*4882a593Smuzhiyun "Unit Chain 0x%x\n",
784*4882a593Smuzhiyun block / (inftl->EraseSize / SECTORSIZE));
785*4882a593Smuzhiyun return 1;
786*4882a593Smuzhiyun }
787*4882a593Smuzhiyun thisEUN = inftl->PUtable[thisEUN];
788*4882a593Smuzhiyun }
789*4882a593Smuzhiyun
790*4882a593Smuzhiyun foundit:
791*4882a593Smuzhiyun if (thisEUN != BLOCK_NIL) {
792*4882a593Smuzhiyun loff_t ptr = (thisEUN * inftl->EraseSize) + blockofs;
793*4882a593Smuzhiyun
794*4882a593Smuzhiyun if (inftl_read_oob(mtd, ptr, 8, &retlen, (char *)&bci) < 0)
795*4882a593Smuzhiyun return -EIO;
796*4882a593Smuzhiyun bci.Status = bci.Status1 = SECTOR_DELETED;
797*4882a593Smuzhiyun if (inftl_write_oob(mtd, ptr, 8, &retlen, (char *)&bci) < 0)
798*4882a593Smuzhiyun return -EIO;
799*4882a593Smuzhiyun INFTL_trydeletechain(inftl, block / (inftl->EraseSize / SECTORSIZE));
800*4882a593Smuzhiyun }
801*4882a593Smuzhiyun return 0;
802*4882a593Smuzhiyun }
803*4882a593Smuzhiyun
inftl_writeblock(struct mtd_blktrans_dev * mbd,unsigned long block,char * buffer)804*4882a593Smuzhiyun static int inftl_writeblock(struct mtd_blktrans_dev *mbd, unsigned long block,
805*4882a593Smuzhiyun char *buffer)
806*4882a593Smuzhiyun {
807*4882a593Smuzhiyun struct INFTLrecord *inftl = (void *)mbd;
808*4882a593Smuzhiyun unsigned int writeEUN;
809*4882a593Smuzhiyun unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize - 1);
810*4882a593Smuzhiyun size_t retlen;
811*4882a593Smuzhiyun struct inftl_oob oob;
812*4882a593Smuzhiyun char *p, *pend;
813*4882a593Smuzhiyun
814*4882a593Smuzhiyun pr_debug("INFTL: inftl_writeblock(inftl=%p,block=%ld,"
815*4882a593Smuzhiyun "buffer=%p)\n", inftl, block, buffer);
816*4882a593Smuzhiyun
817*4882a593Smuzhiyun /* Is block all zero? */
818*4882a593Smuzhiyun pend = buffer + SECTORSIZE;
819*4882a593Smuzhiyun for (p = buffer; p < pend && !*p; p++)
820*4882a593Smuzhiyun ;
821*4882a593Smuzhiyun
822*4882a593Smuzhiyun if (p < pend) {
823*4882a593Smuzhiyun writeEUN = INFTL_findwriteunit(inftl, block);
824*4882a593Smuzhiyun
825*4882a593Smuzhiyun if (writeEUN == BLOCK_NIL) {
826*4882a593Smuzhiyun printk(KERN_WARNING "inftl_writeblock(): cannot find "
827*4882a593Smuzhiyun "block to write to\n");
828*4882a593Smuzhiyun /*
829*4882a593Smuzhiyun * If we _still_ haven't got a block to use,
830*4882a593Smuzhiyun * we're screwed.
831*4882a593Smuzhiyun */
832*4882a593Smuzhiyun return 1;
833*4882a593Smuzhiyun }
834*4882a593Smuzhiyun
835*4882a593Smuzhiyun memset(&oob, 0xff, sizeof(struct inftl_oob));
836*4882a593Smuzhiyun oob.b.Status = oob.b.Status1 = SECTOR_USED;
837*4882a593Smuzhiyun
838*4882a593Smuzhiyun inftl_write(inftl->mbd.mtd, (writeEUN * inftl->EraseSize) +
839*4882a593Smuzhiyun blockofs, SECTORSIZE, &retlen, (char *)buffer,
840*4882a593Smuzhiyun (char *)&oob);
841*4882a593Smuzhiyun /*
842*4882a593Smuzhiyun * need to write SECTOR_USED flags since they are not written
843*4882a593Smuzhiyun * in mtd_writeecc
844*4882a593Smuzhiyun */
845*4882a593Smuzhiyun } else {
846*4882a593Smuzhiyun INFTL_deleteblock(inftl, block);
847*4882a593Smuzhiyun }
848*4882a593Smuzhiyun
849*4882a593Smuzhiyun return 0;
850*4882a593Smuzhiyun }
851*4882a593Smuzhiyun
inftl_readblock(struct mtd_blktrans_dev * mbd,unsigned long block,char * buffer)852*4882a593Smuzhiyun static int inftl_readblock(struct mtd_blktrans_dev *mbd, unsigned long block,
853*4882a593Smuzhiyun char *buffer)
854*4882a593Smuzhiyun {
855*4882a593Smuzhiyun struct INFTLrecord *inftl = (void *)mbd;
856*4882a593Smuzhiyun unsigned int thisEUN = inftl->VUtable[block / (inftl->EraseSize / SECTORSIZE)];
857*4882a593Smuzhiyun unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize - 1);
858*4882a593Smuzhiyun struct mtd_info *mtd = inftl->mbd.mtd;
859*4882a593Smuzhiyun unsigned int status;
860*4882a593Smuzhiyun int silly = MAX_LOOPS;
861*4882a593Smuzhiyun struct inftl_bci bci;
862*4882a593Smuzhiyun size_t retlen;
863*4882a593Smuzhiyun
864*4882a593Smuzhiyun pr_debug("INFTL: inftl_readblock(inftl=%p,block=%ld,"
865*4882a593Smuzhiyun "buffer=%p)\n", inftl, block, buffer);
866*4882a593Smuzhiyun
867*4882a593Smuzhiyun while (thisEUN < inftl->nb_blocks) {
868*4882a593Smuzhiyun if (inftl_read_oob(mtd, (thisEUN * inftl->EraseSize) +
869*4882a593Smuzhiyun blockofs, 8, &retlen, (char *)&bci) < 0)
870*4882a593Smuzhiyun status = SECTOR_IGNORE;
871*4882a593Smuzhiyun else
872*4882a593Smuzhiyun status = bci.Status | bci.Status1;
873*4882a593Smuzhiyun
874*4882a593Smuzhiyun switch (status) {
875*4882a593Smuzhiyun case SECTOR_DELETED:
876*4882a593Smuzhiyun thisEUN = BLOCK_NIL;
877*4882a593Smuzhiyun goto foundit;
878*4882a593Smuzhiyun case SECTOR_USED:
879*4882a593Smuzhiyun goto foundit;
880*4882a593Smuzhiyun case SECTOR_FREE:
881*4882a593Smuzhiyun case SECTOR_IGNORE:
882*4882a593Smuzhiyun break;
883*4882a593Smuzhiyun default:
884*4882a593Smuzhiyun printk(KERN_WARNING "INFTL: unknown status for "
885*4882a593Smuzhiyun "block %ld in EUN %d: 0x%04x\n",
886*4882a593Smuzhiyun block, thisEUN, status);
887*4882a593Smuzhiyun break;
888*4882a593Smuzhiyun }
889*4882a593Smuzhiyun
890*4882a593Smuzhiyun if (!silly--) {
891*4882a593Smuzhiyun printk(KERN_WARNING "INFTL: infinite loop in "
892*4882a593Smuzhiyun "Virtual Unit Chain 0x%lx\n",
893*4882a593Smuzhiyun block / (inftl->EraseSize / SECTORSIZE));
894*4882a593Smuzhiyun return 1;
895*4882a593Smuzhiyun }
896*4882a593Smuzhiyun
897*4882a593Smuzhiyun thisEUN = inftl->PUtable[thisEUN];
898*4882a593Smuzhiyun }
899*4882a593Smuzhiyun
900*4882a593Smuzhiyun foundit:
901*4882a593Smuzhiyun if (thisEUN == BLOCK_NIL) {
902*4882a593Smuzhiyun /* The requested block is not on the media, return all 0x00 */
903*4882a593Smuzhiyun memset(buffer, 0, SECTORSIZE);
904*4882a593Smuzhiyun } else {
905*4882a593Smuzhiyun size_t retlen;
906*4882a593Smuzhiyun loff_t ptr = (thisEUN * inftl->EraseSize) + blockofs;
907*4882a593Smuzhiyun int ret = mtd_read(mtd, ptr, SECTORSIZE, &retlen, buffer);
908*4882a593Smuzhiyun
909*4882a593Smuzhiyun /* Handle corrected bit flips gracefully */
910*4882a593Smuzhiyun if (ret < 0 && !mtd_is_bitflip(ret))
911*4882a593Smuzhiyun return -EIO;
912*4882a593Smuzhiyun }
913*4882a593Smuzhiyun return 0;
914*4882a593Smuzhiyun }
915*4882a593Smuzhiyun
inftl_getgeo(struct mtd_blktrans_dev * dev,struct hd_geometry * geo)916*4882a593Smuzhiyun static int inftl_getgeo(struct mtd_blktrans_dev *dev, struct hd_geometry *geo)
917*4882a593Smuzhiyun {
918*4882a593Smuzhiyun struct INFTLrecord *inftl = (void *)dev;
919*4882a593Smuzhiyun
920*4882a593Smuzhiyun geo->heads = inftl->heads;
921*4882a593Smuzhiyun geo->sectors = inftl->sectors;
922*4882a593Smuzhiyun geo->cylinders = inftl->cylinders;
923*4882a593Smuzhiyun
924*4882a593Smuzhiyun return 0;
925*4882a593Smuzhiyun }
926*4882a593Smuzhiyun
927*4882a593Smuzhiyun static struct mtd_blktrans_ops inftl_tr = {
928*4882a593Smuzhiyun .name = "inftl",
929*4882a593Smuzhiyun .major = INFTL_MAJOR,
930*4882a593Smuzhiyun .part_bits = INFTL_PARTN_BITS,
931*4882a593Smuzhiyun .blksize = 512,
932*4882a593Smuzhiyun .getgeo = inftl_getgeo,
933*4882a593Smuzhiyun .readsect = inftl_readblock,
934*4882a593Smuzhiyun .writesect = inftl_writeblock,
935*4882a593Smuzhiyun .add_mtd = inftl_add_mtd,
936*4882a593Smuzhiyun .remove_dev = inftl_remove_dev,
937*4882a593Smuzhiyun .owner = THIS_MODULE,
938*4882a593Smuzhiyun };
939*4882a593Smuzhiyun
init_inftl(void)940*4882a593Smuzhiyun static int __init init_inftl(void)
941*4882a593Smuzhiyun {
942*4882a593Smuzhiyun return register_mtd_blktrans(&inftl_tr);
943*4882a593Smuzhiyun }
944*4882a593Smuzhiyun
cleanup_inftl(void)945*4882a593Smuzhiyun static void __exit cleanup_inftl(void)
946*4882a593Smuzhiyun {
947*4882a593Smuzhiyun deregister_mtd_blktrans(&inftl_tr);
948*4882a593Smuzhiyun }
949*4882a593Smuzhiyun
950*4882a593Smuzhiyun module_init(init_inftl);
951*4882a593Smuzhiyun module_exit(cleanup_inftl);
952*4882a593Smuzhiyun
953*4882a593Smuzhiyun MODULE_LICENSE("GPL");
954*4882a593Smuzhiyun MODULE_AUTHOR("Greg Ungerer <gerg@snapgear.com>, David Woodhouse <dwmw2@infradead.org>, Fabrice Bellard <fabrice.bellard@netgem.com> et al.");
955*4882a593Smuzhiyun MODULE_DESCRIPTION("Support code for Inverse Flash Translation Layer, used on M-Systems DiskOnChip 2000, Millennium and Millennium Plus");
956