xref: /linux/drivers/mtd/rfd_ftl.c (revision a0faf5fd)
109c434b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2e27a9960SSean Young /*
3e27a9960SSean Young  * rfd_ftl.c -- resident flash disk (flash translation layer)
4e27a9960SSean Young  *
5a1452a37SDavid Woodhouse  * Copyright © 2005  Sean Young <sean@mess.org>
6e27a9960SSean Young  *
7e27a9960SSean Young  * This type of flash translation layer (FTL) is used by the Embedded BIOS
8e27a9960SSean Young  * by General Software. It is known as the Resident Flash Disk (RFD), see:
9e27a9960SSean Young  *
10e27a9960SSean Young  *	http://www.gensw.com/pages/prod/bios/rfd.htm
11e27a9960SSean Young  *
12e27a9960SSean Young  * based on ftl.c
13e27a9960SSean Young  */
14e27a9960SSean Young 
15e27a9960SSean Young #include <linux/hdreg.h>
16e27a9960SSean Young #include <linux/init.h>
17e27a9960SSean Young #include <linux/mtd/blktrans.h>
18e27a9960SSean Young #include <linux/mtd/mtd.h>
19e27a9960SSean Young #include <linux/vmalloc.h>
20de25968cSTim Schmielau #include <linux/slab.h>
21b80b5832SAndrew Morton #include <linux/jiffies.h>
22a0e5cc58SPaul Gortmaker #include <linux/module.h>
23e27a9960SSean Young 
24e27a9960SSean Young #include <asm/types.h>
25e27a9960SSean Young 
26e27a9960SSean Young static int block_size = 0;
27e27a9960SSean Young module_param(block_size, int, 0);
28e27a9960SSean Young MODULE_PARM_DESC(block_size, "Block size to use by RFD, defaults to erase unit size");
29e27a9960SSean Young 
30e27a9960SSean Young #define PREFIX "rfd_ftl: "
31e27a9960SSean Young 
32bc4117f8SSean Young /* This major has been assigned by device@lanana.org */
33e27a9960SSean Young #ifndef RFD_FTL_MAJOR
34bc4117f8SSean Young #define RFD_FTL_MAJOR		256
35e27a9960SSean Young #endif
36e27a9960SSean Young 
37e27a9960SSean Young /* Maximum number of partitions in an FTL region */
38e27a9960SSean Young #define PART_BITS		4
39e27a9960SSean Young 
40e27a9960SSean Young /* An erase unit should start with this value */
41e27a9960SSean Young #define RFD_MAGIC		0x9193
42e27a9960SSean Young 
43e27a9960SSean Young /* the second value is 0xffff or 0xffc8; function unknown */
44e27a9960SSean Young 
45e27a9960SSean Young /* the third value is always 0xffff, ignored */
46e27a9960SSean Young 
47e27a9960SSean Young /* next is an array of mapping for each corresponding sector */
48e27a9960SSean Young #define HEADER_MAP_OFFSET	3
49e27a9960SSean Young #define SECTOR_DELETED		0x0000
50e27a9960SSean Young #define SECTOR_ZERO		0xfffe
51e27a9960SSean Young #define SECTOR_FREE		0xffff
52e27a9960SSean Young 
53e27a9960SSean Young #define SECTOR_SIZE		512
54e27a9960SSean Young 
55e27a9960SSean Young #define SECTORS_PER_TRACK	63
56e27a9960SSean Young 
57e27a9960SSean Young struct block {
58e27a9960SSean Young 	enum {
59e27a9960SSean Young 		BLOCK_OK,
60e27a9960SSean Young 		BLOCK_ERASING,
61e27a9960SSean Young 		BLOCK_ERASED,
62683b30c8SSean Young 		BLOCK_UNUSED,
63e27a9960SSean Young 		BLOCK_FAILED
64e27a9960SSean Young 	} state;
65e27a9960SSean Young 	int free_sectors;
66e27a9960SSean Young 	int used_sectors;
67e27a9960SSean Young 	int erases;
68e27a9960SSean Young 	u_long offset;
69e27a9960SSean Young };
70e27a9960SSean Young 
71e27a9960SSean Young struct partition {
72e27a9960SSean Young 	struct mtd_blktrans_dev mbd;
73e27a9960SSean Young 
74e27a9960SSean Young 	u_int block_size;		/* size of erase unit */
75e27a9960SSean Young 	u_int total_blocks;		/* number of erase units */
76e27a9960SSean Young 	u_int header_sectors_per_block;	/* header sectors in erase unit */
77e27a9960SSean Young 	u_int data_sectors_per_block;	/* data sectors in erase unit */
78e27a9960SSean Young 	u_int sector_count;		/* sectors in translated disk */
79e27a9960SSean Young 	u_int header_size;		/* bytes in header sector */
80e27a9960SSean Young 	int reserved_block;		/* block next up for reclaim */
81e27a9960SSean Young 	int current_block;		/* block to write to */
82e27a9960SSean Young 	u16 *header_cache;		/* cached header */
83e27a9960SSean Young 
84e27a9960SSean Young 	int is_reclaiming;
85e27a9960SSean Young 	int cylinders;
86e27a9960SSean Young 	int errors;
87e27a9960SSean Young 	u_long *sector_map;
88e27a9960SSean Young 	struct block *blocks;
89e27a9960SSean Young };
90e27a9960SSean Young 
91e27a9960SSean Young static int rfd_ftl_writesect(struct mtd_blktrans_dev *dev, u_long sector, char *buf);
92e27a9960SSean Young 
build_block_map(struct partition * part,int block_no)93e27a9960SSean Young static int build_block_map(struct partition *part, int block_no)
94e27a9960SSean Young {
95e27a9960SSean Young 	struct block *block = &part->blocks[block_no];
96e27a9960SSean Young 	int i;
97e27a9960SSean Young 
98e27a9960SSean Young 	block->offset = part->block_size * block_no;
99e27a9960SSean Young 
100e27a9960SSean Young 	if (le16_to_cpu(part->header_cache[0]) != RFD_MAGIC) {
101683b30c8SSean Young 		block->state = BLOCK_UNUSED;
102683b30c8SSean Young 		return -ENOENT;
103e27a9960SSean Young 	}
104e27a9960SSean Young 
105e27a9960SSean Young 	block->state = BLOCK_OK;
106e27a9960SSean Young 
107e27a9960SSean Young 	for (i=0; i<part->data_sectors_per_block; i++) {
108e27a9960SSean Young 		u16 entry;
109e27a9960SSean Young 
110e27a9960SSean Young 		entry = le16_to_cpu(part->header_cache[HEADER_MAP_OFFSET + i]);
111e27a9960SSean Young 
112e27a9960SSean Young 		if (entry == SECTOR_DELETED)
113e27a9960SSean Young 			continue;
114e27a9960SSean Young 
115e27a9960SSean Young 		if (entry == SECTOR_FREE) {
116e27a9960SSean Young 			block->free_sectors++;
117e27a9960SSean Young 			continue;
118e27a9960SSean Young 		}
119e27a9960SSean Young 
120e27a9960SSean Young 		if (entry == SECTOR_ZERO)
121e27a9960SSean Young 			entry = 0;
122e27a9960SSean Young 
123e27a9960SSean Young 		if (entry >= part->sector_count) {
124683b30c8SSean Young 			printk(KERN_WARNING PREFIX
125e27a9960SSean Young 				"'%s': unit #%d: entry %d corrupt, "
126e27a9960SSean Young 				"sector %d out of range\n",
127e27a9960SSean Young 				part->mbd.mtd->name, block_no, i, entry);
128e27a9960SSean Young 			continue;
129e27a9960SSean Young 		}
130e27a9960SSean Young 
131e27a9960SSean Young 		if (part->sector_map[entry] != -1) {
132683b30c8SSean Young 			printk(KERN_WARNING PREFIX
133e27a9960SSean Young 				"'%s': more than one entry for sector %d\n",
134e27a9960SSean Young 				part->mbd.mtd->name, entry);
135e27a9960SSean Young 			part->errors = 1;
136e27a9960SSean Young 			continue;
137e27a9960SSean Young 		}
138e27a9960SSean Young 
139e27a9960SSean Young 		part->sector_map[entry] = block->offset +
140e27a9960SSean Young 			(i + part->header_sectors_per_block) * SECTOR_SIZE;
141e27a9960SSean Young 
142e27a9960SSean Young 		block->used_sectors++;
143e27a9960SSean Young 	}
144e27a9960SSean Young 
145e27a9960SSean Young 	if (block->free_sectors == part->data_sectors_per_block)
146e27a9960SSean Young 		part->reserved_block = block_no;
147e27a9960SSean Young 
148e27a9960SSean Young 	return 0;
149e27a9960SSean Young }
150e27a9960SSean Young 
scan_header(struct partition * part)151e27a9960SSean Young static int scan_header(struct partition *part)
152e27a9960SSean Young {
153e27a9960SSean Young 	int sectors_per_block;
154e27a9960SSean Young 	int i, rc = -ENOMEM;
155e27a9960SSean Young 	int blocks_found;
156e27a9960SSean Young 	size_t retlen;
157e27a9960SSean Young 
158e27a9960SSean Young 	sectors_per_block = part->block_size / SECTOR_SIZE;
15969423d99SAdrian Hunter 	part->total_blocks = (u32)part->mbd.mtd->size / part->block_size;
160e27a9960SSean Young 
161e27a9960SSean Young 	if (part->total_blocks < 2)
162e27a9960SSean Young 		return -ENOENT;
163e27a9960SSean Young 
164e27a9960SSean Young 	/* each erase block has three bytes header, followed by the map */
165e27a9960SSean Young 	part->header_sectors_per_block =
166e27a9960SSean Young 			((HEADER_MAP_OFFSET + sectors_per_block) *
167e27a9960SSean Young 			sizeof(u16) + SECTOR_SIZE - 1) / SECTOR_SIZE;
168e27a9960SSean Young 
169e27a9960SSean Young 	part->data_sectors_per_block = sectors_per_block -
170e27a9960SSean Young 			part->header_sectors_per_block;
171e27a9960SSean Young 
172e27a9960SSean Young 	part->header_size = (HEADER_MAP_OFFSET +
173e27a9960SSean Young 			part->data_sectors_per_block) * sizeof(u16);
174e27a9960SSean Young 
175e27a9960SSean Young 	part->cylinders = (part->data_sectors_per_block *
176e27a9960SSean Young 			(part->total_blocks - 1) - 1) / SECTORS_PER_TRACK;
177e27a9960SSean Young 
178e27a9960SSean Young 	part->sector_count = part->cylinders * SECTORS_PER_TRACK;
179e27a9960SSean Young 
180e27a9960SSean Young 	part->current_block = -1;
181e27a9960SSean Young 	part->reserved_block = -1;
182e27a9960SSean Young 	part->is_reclaiming = 0;
183e27a9960SSean Young 
184e27a9960SSean Young 	part->header_cache = kmalloc(part->header_size, GFP_KERNEL);
185e27a9960SSean Young 	if (!part->header_cache)
186e27a9960SSean Young 		goto err;
187e27a9960SSean Young 
188e27a9960SSean Young 	part->blocks = kcalloc(part->total_blocks, sizeof(struct block),
189e27a9960SSean Young 			GFP_KERNEL);
190e27a9960SSean Young 	if (!part->blocks)
191e27a9960SSean Young 		goto err;
192e27a9960SSean Young 
19342bc47b3SKees Cook 	part->sector_map = vmalloc(array_size(sizeof(u_long),
19442bc47b3SKees Cook 					      part->sector_count));
195cba8b3bcSZhen Lei 	if (!part->sector_map)
196e27a9960SSean Young 		goto err;
197e27a9960SSean Young 
198e27a9960SSean Young 	for (i=0; i<part->sector_count; i++)
199e27a9960SSean Young 		part->sector_map[i] = -1;
200e27a9960SSean Young 
201e27a9960SSean Young 	for (i=0, blocks_found=0; i<part->total_blocks; i++) {
202329ad399SArtem Bityutskiy 		rc = mtd_read(part->mbd.mtd, i * part->block_size,
203329ad399SArtem Bityutskiy 			      part->header_size, &retlen,
204329ad399SArtem Bityutskiy 			      (u_char *)part->header_cache);
205e27a9960SSean Young 
206e27a9960SSean Young 		if (!rc && retlen != part->header_size)
207e27a9960SSean Young 			rc = -EIO;
208e27a9960SSean Young 
209e27a9960SSean Young 		if (rc)
210e27a9960SSean Young 			goto err;
211e27a9960SSean Young 
212e27a9960SSean Young 		if (!build_block_map(part, i))
213e27a9960SSean Young 			blocks_found++;
214e27a9960SSean Young 	}
215e27a9960SSean Young 
216e27a9960SSean Young 	if (blocks_found == 0) {
217e27a9960SSean Young 		printk(KERN_NOTICE PREFIX "no RFD magic found in '%s'\n",
218e27a9960SSean Young 				part->mbd.mtd->name);
219e27a9960SSean Young 		rc = -ENOENT;
220e27a9960SSean Young 		goto err;
221e27a9960SSean Young 	}
222e27a9960SSean Young 
223e27a9960SSean Young 	if (part->reserved_block == -1) {
224683b30c8SSean Young 		printk(KERN_WARNING PREFIX "'%s': no empty erase unit found\n",
225e27a9960SSean Young 				part->mbd.mtd->name);
226e27a9960SSean Young 
227e27a9960SSean Young 		part->errors = 1;
228e27a9960SSean Young 	}
229e27a9960SSean Young 
230e27a9960SSean Young 	return 0;
231e27a9960SSean Young 
232e27a9960SSean Young err:
233e27a9960SSean Young 	vfree(part->sector_map);
234e27a9960SSean Young 	kfree(part->header_cache);
235e27a9960SSean Young 	kfree(part->blocks);
236e27a9960SSean Young 
237e27a9960SSean Young 	return rc;
238e27a9960SSean Young }
239e27a9960SSean Young 
rfd_ftl_readsect(struct mtd_blktrans_dev * dev,u_long sector,char * buf)240e27a9960SSean Young static int rfd_ftl_readsect(struct mtd_blktrans_dev *dev, u_long sector, char *buf)
241e27a9960SSean Young {
242fa451399SSean Young 	struct partition *part = container_of(dev, struct partition, mbd);
243e27a9960SSean Young 	u_long addr;
244e27a9960SSean Young 	size_t retlen;
245e27a9960SSean Young 	int rc;
246e27a9960SSean Young 
247e27a9960SSean Young 	if (sector >= part->sector_count)
248e27a9960SSean Young 		return -EIO;
249e27a9960SSean Young 
250e27a9960SSean Young 	addr = part->sector_map[sector];
251e27a9960SSean Young 	if (addr != -1) {
252329ad399SArtem Bityutskiy 		rc = mtd_read(part->mbd.mtd, addr, SECTOR_SIZE, &retlen,
253329ad399SArtem Bityutskiy 			      (u_char *)buf);
254e27a9960SSean Young 		if (!rc && retlen != SECTOR_SIZE)
255e27a9960SSean Young 			rc = -EIO;
256e27a9960SSean Young 
257e27a9960SSean Young 		if (rc) {
258e27a9960SSean Young 			printk(KERN_WARNING PREFIX "error reading '%s' at "
259e27a9960SSean Young 				"0x%lx\n", part->mbd.mtd->name, addr);
260e27a9960SSean Young 			return rc;
261e27a9960SSean Young 		}
262e27a9960SSean Young 	} else
263e27a9960SSean Young 		memset(buf, 0, SECTOR_SIZE);
264e27a9960SSean Young 
265e27a9960SSean Young 	return 0;
266e27a9960SSean Young }
267e27a9960SSean Young 
erase_block(struct partition * part,int block)268e27a9960SSean Young static int erase_block(struct partition *part, int block)
269e27a9960SSean Young {
270e27a9960SSean Young 	struct erase_info *erase;
271884cfd90SBoris Brezillon 	int rc;
272e27a9960SSean Young 
273e27a9960SSean Young 	erase = kmalloc(sizeof(struct erase_info), GFP_KERNEL);
274e27a9960SSean Young 	if (!erase)
275884cfd90SBoris Brezillon 		return -ENOMEM;
276e27a9960SSean Young 
277e27a9960SSean Young 	erase->addr = part->blocks[block].offset;
278e27a9960SSean Young 	erase->len = part->block_size;
279e27a9960SSean Young 
280e27a9960SSean Young 	part->blocks[block].state = BLOCK_ERASING;
281e27a9960SSean Young 	part->blocks[block].free_sectors = 0;
282e27a9960SSean Young 
2837e1f0dc0SArtem Bityutskiy 	rc = mtd_erase(part->mbd.mtd, erase);
284e27a9960SSean Young 	if (rc) {
28569423d99SAdrian Hunter 		printk(KERN_ERR PREFIX "erase of region %llx,%llx on '%s' "
28669423d99SAdrian Hunter 				"failed\n", (unsigned long long)erase->addr,
28769423d99SAdrian Hunter 				(unsigned long long)erase->len, part->mbd.mtd->name);
288884cfd90SBoris Brezillon 		part->blocks[block].state = BLOCK_FAILED;
289884cfd90SBoris Brezillon 		part->blocks[block].free_sectors = 0;
290884cfd90SBoris Brezillon 		part->blocks[block].used_sectors = 0;
291884cfd90SBoris Brezillon 	} else {
292884cfd90SBoris Brezillon 		u16 magic = cpu_to_le16(RFD_MAGIC);
293884cfd90SBoris Brezillon 		size_t retlen;
294884cfd90SBoris Brezillon 
295884cfd90SBoris Brezillon 		part->blocks[block].state = BLOCK_ERASED;
296884cfd90SBoris Brezillon 		part->blocks[block].free_sectors = part->data_sectors_per_block;
297884cfd90SBoris Brezillon 		part->blocks[block].used_sectors = 0;
298884cfd90SBoris Brezillon 		part->blocks[block].erases++;
299884cfd90SBoris Brezillon 
300884cfd90SBoris Brezillon 		rc = mtd_write(part->mbd.mtd, part->blocks[block].offset,
301884cfd90SBoris Brezillon 			       sizeof(magic), &retlen, (u_char *)&magic);
302884cfd90SBoris Brezillon 		if (!rc && retlen != sizeof(magic))
303884cfd90SBoris Brezillon 			rc = -EIO;
304884cfd90SBoris Brezillon 
305884cfd90SBoris Brezillon 		if (rc) {
306884cfd90SBoris Brezillon 			pr_err(PREFIX "'%s': unable to write RFD header at 0x%lx\n",
307884cfd90SBoris Brezillon 			       part->mbd.mtd->name, part->blocks[block].offset);
308884cfd90SBoris Brezillon 			part->blocks[block].state = BLOCK_FAILED;
309884cfd90SBoris Brezillon 		} else {
310884cfd90SBoris Brezillon 			part->blocks[block].state = BLOCK_OK;
311884cfd90SBoris Brezillon 		}
312e27a9960SSean Young 	}
313e27a9960SSean Young 
314884cfd90SBoris Brezillon 	kfree(erase);
315884cfd90SBoris Brezillon 
316e27a9960SSean Young 	return rc;
317e27a9960SSean Young }
318e27a9960SSean Young 
move_block_contents(struct partition * part,int block_no,u_long * old_sector)319e27a9960SSean Young static int move_block_contents(struct partition *part, int block_no, u_long *old_sector)
320e27a9960SSean Young {
321e27a9960SSean Young 	void *sector_data;
322e27a9960SSean Young 	u16 *map;
323e27a9960SSean Young 	size_t retlen;
324e27a9960SSean Young 	int i, rc = -ENOMEM;
325e27a9960SSean Young 
326e27a9960SSean Young 	part->is_reclaiming = 1;
327e27a9960SSean Young 
328e27a9960SSean Young 	sector_data = kmalloc(SECTOR_SIZE, GFP_KERNEL);
329e27a9960SSean Young 	if (!sector_data)
330e27a9960SSean Young 		goto err3;
331e27a9960SSean Young 
332e27a9960SSean Young 	map = kmalloc(part->header_size, GFP_KERNEL);
333e27a9960SSean Young 	if (!map)
334e27a9960SSean Young 		goto err2;
335e27a9960SSean Young 
336329ad399SArtem Bityutskiy 	rc = mtd_read(part->mbd.mtd, part->blocks[block_no].offset,
337329ad399SArtem Bityutskiy 		      part->header_size, &retlen, (u_char *)map);
338e27a9960SSean Young 
339e27a9960SSean Young 	if (!rc && retlen != part->header_size)
340e27a9960SSean Young 		rc = -EIO;
341e27a9960SSean Young 
342e27a9960SSean Young 	if (rc) {
343683b30c8SSean Young 		printk(KERN_ERR PREFIX "error reading '%s' at "
344e27a9960SSean Young 			"0x%lx\n", part->mbd.mtd->name,
345e27a9960SSean Young 			part->blocks[block_no].offset);
346e27a9960SSean Young 
347e27a9960SSean Young 		goto err;
348e27a9960SSean Young 	}
349e27a9960SSean Young 
350e27a9960SSean Young 	for (i=0; i<part->data_sectors_per_block; i++) {
351e27a9960SSean Young 		u16 entry = le16_to_cpu(map[HEADER_MAP_OFFSET + i]);
352e27a9960SSean Young 		u_long addr;
353e27a9960SSean Young 
354e27a9960SSean Young 
355e27a9960SSean Young 		if (entry == SECTOR_FREE || entry == SECTOR_DELETED)
356e27a9960SSean Young 			continue;
357e27a9960SSean Young 
358e27a9960SSean Young 		if (entry == SECTOR_ZERO)
359e27a9960SSean Young 			entry = 0;
360e27a9960SSean Young 
361e27a9960SSean Young 		/* already warned about and ignored in build_block_map() */
362e27a9960SSean Young 		if (entry >= part->sector_count)
363e27a9960SSean Young 			continue;
364e27a9960SSean Young 
365e27a9960SSean Young 		addr = part->blocks[block_no].offset +
366e27a9960SSean Young 			(i + part->header_sectors_per_block) * SECTOR_SIZE;
367e27a9960SSean Young 
368e27a9960SSean Young 		if (*old_sector == addr) {
369e27a9960SSean Young 			*old_sector = -1;
370e27a9960SSean Young 			if (!part->blocks[block_no].used_sectors--) {
371e27a9960SSean Young 				rc = erase_block(part, block_no);
372e27a9960SSean Young 				break;
373e27a9960SSean Young 			}
374e27a9960SSean Young 			continue;
375e27a9960SSean Young 		}
376329ad399SArtem Bityutskiy 		rc = mtd_read(part->mbd.mtd, addr, SECTOR_SIZE, &retlen,
377329ad399SArtem Bityutskiy 			      sector_data);
378e27a9960SSean Young 
379e27a9960SSean Young 		if (!rc && retlen != SECTOR_SIZE)
380e27a9960SSean Young 			rc = -EIO;
381e27a9960SSean Young 
382e27a9960SSean Young 		if (rc) {
383683b30c8SSean Young 			printk(KERN_ERR PREFIX "'%s': Unable to "
384e27a9960SSean Young 				"read sector for relocation\n",
385e27a9960SSean Young 				part->mbd.mtd->name);
386e27a9960SSean Young 
387e27a9960SSean Young 			goto err;
388e27a9960SSean Young 		}
389e27a9960SSean Young 
390e27a9960SSean Young 		rc = rfd_ftl_writesect((struct mtd_blktrans_dev*)part,
391e27a9960SSean Young 				entry, sector_data);
392e27a9960SSean Young 
393e27a9960SSean Young 		if (rc)
394e27a9960SSean Young 			goto err;
395e27a9960SSean Young 	}
396e27a9960SSean Young 
397e27a9960SSean Young err:
398e27a9960SSean Young 	kfree(map);
399e27a9960SSean Young err2:
400e27a9960SSean Young 	kfree(sector_data);
401e27a9960SSean Young err3:
402e27a9960SSean Young 	part->is_reclaiming = 0;
403e27a9960SSean Young 
404e27a9960SSean Young 	return rc;
405e27a9960SSean Young }
406e27a9960SSean Young 
reclaim_block(struct partition * part,u_long * old_sector)407e27a9960SSean Young static int reclaim_block(struct partition *part, u_long *old_sector)
408e27a9960SSean Young {
409e27a9960SSean Young 	int block, best_block, score, old_sector_block;
410e27a9960SSean Young 	int rc;
411e27a9960SSean Young 
412e27a9960SSean Young 	/* we have a race if sync doesn't exist */
41385f2f2a8SArtem Bityutskiy 	mtd_sync(part->mbd.mtd);
414e27a9960SSean Young 
415e27a9960SSean Young 	score = 0x7fffffff; /* MAX_INT */
416e27a9960SSean Young 	best_block = -1;
417e27a9960SSean Young 	if (*old_sector != -1)
418e27a9960SSean Young 		old_sector_block = *old_sector / part->block_size;
419e27a9960SSean Young 	else
420e27a9960SSean Young 		old_sector_block = -1;
421e27a9960SSean Young 
422e27a9960SSean Young 	for (block=0; block<part->total_blocks; block++) {
423e27a9960SSean Young 		int this_score;
424e27a9960SSean Young 
425e27a9960SSean Young 		if (block == part->reserved_block)
426e27a9960SSean Young 			continue;
427e27a9960SSean Young 
428e27a9960SSean Young 		/*
429e27a9960SSean Young 		 * Postpone reclaiming if there is a free sector as
430e27a9960SSean Young 		 * more removed sectors is more efficient (have to move
431e27a9960SSean Young 		 * less).
432e27a9960SSean Young 		 */
433e27a9960SSean Young 		if (part->blocks[block].free_sectors)
434e27a9960SSean Young 			return 0;
435e27a9960SSean Young 
436e27a9960SSean Young 		this_score = part->blocks[block].used_sectors;
437e27a9960SSean Young 
438e27a9960SSean Young 		if (block == old_sector_block)
439e27a9960SSean Young 			this_score--;
440e27a9960SSean Young 		else {
441e27a9960SSean Young 			/* no point in moving a full block */
442e27a9960SSean Young 			if (part->blocks[block].used_sectors ==
443e27a9960SSean Young 					part->data_sectors_per_block)
444e27a9960SSean Young 				continue;
445e27a9960SSean Young 		}
446e27a9960SSean Young 
447e27a9960SSean Young 		this_score += part->blocks[block].erases;
448e27a9960SSean Young 
449e27a9960SSean Young 		if (this_score < score) {
450e27a9960SSean Young 			best_block = block;
451e27a9960SSean Young 			score = this_score;
452e27a9960SSean Young 		}
453e27a9960SSean Young 	}
454e27a9960SSean Young 
455e27a9960SSean Young 	if (best_block == -1)
456e27a9960SSean Young 		return -ENOSPC;
457e27a9960SSean Young 
458e27a9960SSean Young 	part->current_block = -1;
459e27a9960SSean Young 	part->reserved_block = best_block;
460e27a9960SSean Young 
461e27a9960SSean Young 	pr_debug("reclaim_block: reclaiming block #%d with %d used "
462e27a9960SSean Young 		 "%d free sectors\n", best_block,
463e27a9960SSean Young 		 part->blocks[best_block].used_sectors,
464e27a9960SSean Young 		 part->blocks[best_block].free_sectors);
465e27a9960SSean Young 
466e27a9960SSean Young 	if (part->blocks[best_block].used_sectors)
467e27a9960SSean Young 		rc = move_block_contents(part, best_block, old_sector);
468e27a9960SSean Young 	else
469e27a9960SSean Young 		rc = erase_block(part, best_block);
470e27a9960SSean Young 
471e27a9960SSean Young 	return rc;
472e27a9960SSean Young }
473e27a9960SSean Young 
474e27a9960SSean Young /*
475e27a9960SSean Young  * IMPROVE: It would be best to choose the block with the most deleted sectors,
476e27a9960SSean Young  * because if we fill that one up first it'll have the most chance of having
477e27a9960SSean Young  * the least live sectors at reclaim.
478e27a9960SSean Young  */
find_free_block(struct partition * part)479683b30c8SSean Young static int find_free_block(struct partition *part)
480e27a9960SSean Young {
481e27a9960SSean Young 	int block, stop;
482e27a9960SSean Young 
483e27a9960SSean Young 	block = part->current_block == -1 ?
484e27a9960SSean Young 			jiffies % part->total_blocks : part->current_block;
485e27a9960SSean Young 	stop = block;
486e27a9960SSean Young 
487e27a9960SSean Young 	do {
488e27a9960SSean Young 		if (part->blocks[block].free_sectors &&
489e27a9960SSean Young 				block != part->reserved_block)
490e27a9960SSean Young 			return block;
491e27a9960SSean Young 
492683b30c8SSean Young 		if (part->blocks[block].state == BLOCK_UNUSED)
493683b30c8SSean Young 			erase_block(part, block);
494683b30c8SSean Young 
495e27a9960SSean Young 		if (++block >= part->total_blocks)
496e27a9960SSean Young 			block = 0;
497e27a9960SSean Young 
498e27a9960SSean Young 	} while (block != stop);
499e27a9960SSean Young 
500e27a9960SSean Young 	return -1;
501e27a9960SSean Young }
502e27a9960SSean Young 
find_writable_block(struct partition * part,u_long * old_sector)503683b30c8SSean Young static int find_writable_block(struct partition *part, u_long *old_sector)
504e27a9960SSean Young {
505e27a9960SSean Young 	int rc, block;
506e27a9960SSean Young 	size_t retlen;
507e27a9960SSean Young 
508e27a9960SSean Young 	block = find_free_block(part);
509e27a9960SSean Young 
510e27a9960SSean Young 	if (block == -1) {
511e27a9960SSean Young 		if (!part->is_reclaiming) {
512e27a9960SSean Young 			rc = reclaim_block(part, old_sector);
513e27a9960SSean Young 			if (rc)
514e27a9960SSean Young 				goto err;
515e27a9960SSean Young 
516e27a9960SSean Young 			block = find_free_block(part);
517e27a9960SSean Young 		}
518e27a9960SSean Young 
519e27a9960SSean Young 		if (block == -1) {
520e27a9960SSean Young 			rc = -ENOSPC;
521e27a9960SSean Young 			goto err;
522e27a9960SSean Young 		}
523e27a9960SSean Young 	}
524e27a9960SSean Young 
525329ad399SArtem Bityutskiy 	rc = mtd_read(part->mbd.mtd, part->blocks[block].offset,
526329ad399SArtem Bityutskiy 		      part->header_size, &retlen,
527329ad399SArtem Bityutskiy 		      (u_char *)part->header_cache);
528e27a9960SSean Young 
529e27a9960SSean Young 	if (!rc && retlen != part->header_size)
530e27a9960SSean Young 		rc = -EIO;
531e27a9960SSean Young 
532e27a9960SSean Young 	if (rc) {
533683b30c8SSean Young 		printk(KERN_ERR PREFIX "'%s': unable to read header at "
534e27a9960SSean Young 				"0x%lx\n", part->mbd.mtd->name,
535e27a9960SSean Young 				part->blocks[block].offset);
536e27a9960SSean Young 		goto err;
537e27a9960SSean Young 	}
538e27a9960SSean Young 
539e27a9960SSean Young 	part->current_block = block;
540e27a9960SSean Young 
541e27a9960SSean Young err:
542e27a9960SSean Young 	return rc;
543e27a9960SSean Young }
544e27a9960SSean Young 
mark_sector_deleted(struct partition * part,u_long old_addr)545e27a9960SSean Young static int mark_sector_deleted(struct partition *part, u_long old_addr)
546e27a9960SSean Young {
547e27a9960SSean Young 	int block, offset, rc;
548e27a9960SSean Young 	u_long addr;
549e27a9960SSean Young 	size_t retlen;
550c80a7b26SHarvey Harrison 	u16 del = cpu_to_le16(SECTOR_DELETED);
551e27a9960SSean Young 
552e27a9960SSean Young 	block = old_addr / part->block_size;
553e27a9960SSean Young 	offset = (old_addr % part->block_size) / SECTOR_SIZE -
554e27a9960SSean Young 		part->header_sectors_per_block;
555e27a9960SSean Young 
556e27a9960SSean Young 	addr = part->blocks[block].offset +
557e27a9960SSean Young 			(HEADER_MAP_OFFSET + offset) * sizeof(u16);
558eda95cbfSArtem Bityutskiy 	rc = mtd_write(part->mbd.mtd, addr, sizeof(del), &retlen,
559eda95cbfSArtem Bityutskiy 		       (u_char *)&del);
560e27a9960SSean Young 
561e27a9960SSean Young 	if (!rc && retlen != sizeof(del))
562e27a9960SSean Young 		rc = -EIO;
563e27a9960SSean Young 
564e27a9960SSean Young 	if (rc) {
565683b30c8SSean Young 		printk(KERN_ERR PREFIX "error writing '%s' at "
566e27a9960SSean Young 			"0x%lx\n", part->mbd.mtd->name, addr);
567e27a9960SSean Young 		goto err;
568e27a9960SSean Young 	}
569e27a9960SSean Young 	if (block == part->current_block)
570e27a9960SSean Young 		part->header_cache[offset + HEADER_MAP_OFFSET] = del;
571e27a9960SSean Young 
572e27a9960SSean Young 	part->blocks[block].used_sectors--;
573e27a9960SSean Young 
574e27a9960SSean Young 	if (!part->blocks[block].used_sectors &&
575e27a9960SSean Young 	    !part->blocks[block].free_sectors)
576e27a9960SSean Young 		rc = erase_block(part, block);
577e27a9960SSean Young 
578e27a9960SSean Young err:
579e27a9960SSean Young 	return rc;
580e27a9960SSean Young }
581e27a9960SSean Young 
find_free_sector(const struct partition * part,const struct block * block)582e27a9960SSean Young static int find_free_sector(const struct partition *part, const struct block *block)
583e27a9960SSean Young {
584e27a9960SSean Young 	int i, stop;
585e27a9960SSean Young 
586e27a9960SSean Young 	i = stop = part->data_sectors_per_block - block->free_sectors;
587e27a9960SSean Young 
588e27a9960SSean Young 	do {
589e27a9960SSean Young 		if (le16_to_cpu(part->header_cache[HEADER_MAP_OFFSET + i])
590e27a9960SSean Young 				== SECTOR_FREE)
591e27a9960SSean Young 			return i;
592e27a9960SSean Young 
593e27a9960SSean Young 		if (++i == part->data_sectors_per_block)
594e27a9960SSean Young 			i = 0;
595e27a9960SSean Young 	}
596e27a9960SSean Young 	while(i != stop);
597e27a9960SSean Young 
598e27a9960SSean Young 	return -1;
599e27a9960SSean Young }
600e27a9960SSean Young 
do_writesect(struct mtd_blktrans_dev * dev,u_long sector,char * buf,ulong * old_addr)601e27a9960SSean Young static int do_writesect(struct mtd_blktrans_dev *dev, u_long sector, char *buf, ulong *old_addr)
602e27a9960SSean Young {
603fa451399SSean Young 	struct partition *part = container_of(dev, struct partition, mbd);
604e27a9960SSean Young 	struct block *block;
605e27a9960SSean Young 	u_long addr;
606e27a9960SSean Young 	int i;
607e27a9960SSean Young 	int rc;
608e27a9960SSean Young 	size_t retlen;
609e27a9960SSean Young 	u16 entry;
610e27a9960SSean Young 
611e27a9960SSean Young 	if (part->current_block == -1 ||
612e27a9960SSean Young 		!part->blocks[part->current_block].free_sectors) {
613e27a9960SSean Young 
614683b30c8SSean Young 		rc = find_writable_block(part, old_addr);
615e27a9960SSean Young 		if (rc)
616e27a9960SSean Young 			goto err;
617e27a9960SSean Young 	}
618e27a9960SSean Young 
619e27a9960SSean Young 	block = &part->blocks[part->current_block];
620e27a9960SSean Young 
621e27a9960SSean Young 	i = find_free_sector(part, block);
622e27a9960SSean Young 
623e27a9960SSean Young 	if (i < 0) {
624e27a9960SSean Young 		rc = -ENOSPC;
625e27a9960SSean Young 		goto err;
626e27a9960SSean Young 	}
627e27a9960SSean Young 
628e27a9960SSean Young 	addr = (i + part->header_sectors_per_block) * SECTOR_SIZE +
629e27a9960SSean Young 		block->offset;
630eda95cbfSArtem Bityutskiy 	rc = mtd_write(part->mbd.mtd, addr, SECTOR_SIZE, &retlen,
631eda95cbfSArtem Bityutskiy 		       (u_char *)buf);
632e27a9960SSean Young 
633e27a9960SSean Young 	if (!rc && retlen != SECTOR_SIZE)
634e27a9960SSean Young 		rc = -EIO;
635e27a9960SSean Young 
636e27a9960SSean Young 	if (rc) {
637683b30c8SSean Young 		printk(KERN_ERR PREFIX "error writing '%s' at 0x%lx\n",
638e27a9960SSean Young 				part->mbd.mtd->name, addr);
639e27a9960SSean Young 		goto err;
640e27a9960SSean Young 	}
641e27a9960SSean Young 
642e27a9960SSean Young 	part->sector_map[sector] = addr;
643e27a9960SSean Young 
644e27a9960SSean Young 	entry = cpu_to_le16(sector == 0 ? SECTOR_ZERO : sector);
645e27a9960SSean Young 
646e27a9960SSean Young 	part->header_cache[i + HEADER_MAP_OFFSET] = entry;
647e27a9960SSean Young 
648e27a9960SSean Young 	addr = block->offset + (HEADER_MAP_OFFSET + i) * sizeof(u16);
649eda95cbfSArtem Bityutskiy 	rc = mtd_write(part->mbd.mtd, addr, sizeof(entry), &retlen,
650eda95cbfSArtem Bityutskiy 		       (u_char *)&entry);
651e27a9960SSean Young 
652e27a9960SSean Young 	if (!rc && retlen != sizeof(entry))
653e27a9960SSean Young 		rc = -EIO;
654e27a9960SSean Young 
655e27a9960SSean Young 	if (rc) {
656683b30c8SSean Young 		printk(KERN_ERR PREFIX "error writing '%s' at 0x%lx\n",
657e27a9960SSean Young 				part->mbd.mtd->name, addr);
658e27a9960SSean Young 		goto err;
659e27a9960SSean Young 	}
660e27a9960SSean Young 	block->used_sectors++;
661e27a9960SSean Young 	block->free_sectors--;
662e27a9960SSean Young 
663e27a9960SSean Young err:
664e27a9960SSean Young 	return rc;
665e27a9960SSean Young }
666e27a9960SSean Young 
rfd_ftl_writesect(struct mtd_blktrans_dev * dev,u_long sector,char * buf)667e27a9960SSean Young static int rfd_ftl_writesect(struct mtd_blktrans_dev *dev, u_long sector, char *buf)
668e27a9960SSean Young {
669fa451399SSean Young 	struct partition *part = container_of(dev, struct partition, mbd);
670e27a9960SSean Young 	u_long old_addr;
671e27a9960SSean Young 	int i;
672e27a9960SSean Young 	int rc = 0;
673e27a9960SSean Young 
674e27a9960SSean Young 	pr_debug("rfd_ftl_writesect(sector=0x%lx)\n", sector);
675e27a9960SSean Young 
676e27a9960SSean Young 	if (part->reserved_block == -1) {
677e27a9960SSean Young 		rc = -EACCES;
678e27a9960SSean Young 		goto err;
679e27a9960SSean Young 	}
680e27a9960SSean Young 
681e27a9960SSean Young 	if (sector >= part->sector_count) {
682e27a9960SSean Young 		rc = -EIO;
683e27a9960SSean Young 		goto err;
684e27a9960SSean Young 	}
685e27a9960SSean Young 
686e27a9960SSean Young 	old_addr = part->sector_map[sector];
687e27a9960SSean Young 
688e27a9960SSean Young 	for (i=0; i<SECTOR_SIZE; i++) {
689e27a9960SSean Young 		if (!buf[i])
690e27a9960SSean Young 			continue;
691e27a9960SSean Young 
692e27a9960SSean Young 		rc = do_writesect(dev, sector, buf, &old_addr);
693e27a9960SSean Young 		if (rc)
694e27a9960SSean Young 			goto err;
695e27a9960SSean Young 		break;
696e27a9960SSean Young 	}
697e27a9960SSean Young 
698e27a9960SSean Young 	if (i == SECTOR_SIZE)
699e27a9960SSean Young 		part->sector_map[sector] = -1;
700e27a9960SSean Young 
701e27a9960SSean Young 	if (old_addr != -1)
702e27a9960SSean Young 		rc = mark_sector_deleted(part, old_addr);
703e27a9960SSean Young 
704e27a9960SSean Young err:
705e27a9960SSean Young 	return rc;
706e27a9960SSean Young }
707e27a9960SSean Young 
rfd_ftl_discardsect(struct mtd_blktrans_dev * dev,unsigned long sector,unsigned int nr_sects)708a3a44784SSean Young static int rfd_ftl_discardsect(struct mtd_blktrans_dev *dev,
709a3a44784SSean Young 			       unsigned long sector, unsigned int nr_sects)
710a3a44784SSean Young {
711fa451399SSean Young 	struct partition *part = container_of(dev, struct partition, mbd);
712a3a44784SSean Young 	u_long addr;
713a3a44784SSean Young 	int rc;
714a3a44784SSean Young 
715a3a44784SSean Young 	while (nr_sects) {
716a3a44784SSean Young 		if (sector >= part->sector_count)
717a3a44784SSean Young 			return -EIO;
718a3a44784SSean Young 
719a3a44784SSean Young 		addr = part->sector_map[sector];
720a3a44784SSean Young 
721a3a44784SSean Young 		if (addr != -1) {
722a3a44784SSean Young 			rc = mark_sector_deleted(part, addr);
723a3a44784SSean Young 			if (rc)
724a3a44784SSean Young 				return rc;
725a3a44784SSean Young 
726a3a44784SSean Young 			part->sector_map[sector] = -1;
727a3a44784SSean Young 		}
728a3a44784SSean Young 
729a3a44784SSean Young 		sector++;
730a3a44784SSean Young 		nr_sects--;
731a3a44784SSean Young 	}
732a3a44784SSean Young 
733a3a44784SSean Young 	return 0;
734a3a44784SSean Young }
735a3a44784SSean Young 
rfd_ftl_getgeo(struct mtd_blktrans_dev * dev,struct hd_geometry * geo)736e27a9960SSean Young static int rfd_ftl_getgeo(struct mtd_blktrans_dev *dev, struct hd_geometry *geo)
737e27a9960SSean Young {
738fa451399SSean Young 	struct partition *part = container_of(dev, struct partition, mbd);
739e27a9960SSean Young 
740e27a9960SSean Young 	geo->heads = 1;
741e27a9960SSean Young 	geo->sectors = SECTORS_PER_TRACK;
742e27a9960SSean Young 	geo->cylinders = part->cylinders;
743e27a9960SSean Young 
744e27a9960SSean Young 	return 0;
745e27a9960SSean Young }
746e27a9960SSean Young 
rfd_ftl_add_mtd(struct mtd_blktrans_ops * tr,struct mtd_info * mtd)747e27a9960SSean Young static void rfd_ftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
748e27a9960SSean Young {
749e27a9960SSean Young 	struct partition *part;
750e27a9960SSean Young 
751e03a8121SSean Young 	if ((mtd->type != MTD_NORFLASH && mtd->type != MTD_RAM) ||
752e03a8121SSean Young 	    mtd->size > UINT_MAX)
753e27a9960SSean Young 		return;
754e27a9960SSean Young 
755cd861280SRobert P. J. Day 	part = kzalloc(sizeof(struct partition), GFP_KERNEL);
756e27a9960SSean Young 	if (!part)
757e27a9960SSean Young 		return;
758e27a9960SSean Young 
759e27a9960SSean Young 	part->mbd.mtd = mtd;
760e27a9960SSean Young 
761e27a9960SSean Young 	if (block_size)
762e27a9960SSean Young 		part->block_size = block_size;
763e27a9960SSean Young 	else {
764e27a9960SSean Young 		if (!mtd->erasesize) {
765683b30c8SSean Young 			printk(KERN_WARNING PREFIX "please provide block_size");
766030f9e13Sakpm@linux-foundation.org 			goto out;
767030f9e13Sakpm@linux-foundation.org 		} else
768e27a9960SSean Young 			part->block_size = mtd->erasesize;
769e27a9960SSean Young 	}
770e27a9960SSean Young 
771e27a9960SSean Young 	if (scan_header(part) == 0) {
772e27a9960SSean Young 		part->mbd.size = part->sector_count;
773e27a9960SSean Young 		part->mbd.tr = tr;
774e27a9960SSean Young 		part->mbd.devnum = -1;
775e27a9960SSean Young 		if (!(mtd->flags & MTD_WRITEABLE))
776e27a9960SSean Young 			part->mbd.readonly = 1;
777e27a9960SSean Young 		else if (part->errors) {
778683b30c8SSean Young 			printk(KERN_WARNING PREFIX "'%s': errors found, "
779683b30c8SSean Young 					"setting read-only\n", mtd->name);
780e27a9960SSean Young 			part->mbd.readonly = 1;
781e27a9960SSean Young 		}
782e27a9960SSean Young 
783e27a9960SSean Young 		printk(KERN_INFO PREFIX "name: '%s' type: %d flags %x\n",
784e27a9960SSean Young 				mtd->name, mtd->type, mtd->flags);
785e27a9960SSean Young 
786*a0faf5fdSChristoph Hellwig 		if (!add_mtd_blktrans_dev(&part->mbd))
787e27a9960SSean Young 			return;
788e27a9960SSean Young 	}
789030f9e13Sakpm@linux-foundation.org out:
790e27a9960SSean Young 	kfree(part);
791e27a9960SSean Young }
792e27a9960SSean Young 
rfd_ftl_remove_dev(struct mtd_blktrans_dev * dev)793e27a9960SSean Young static void rfd_ftl_remove_dev(struct mtd_blktrans_dev *dev)
794e27a9960SSean Young {
795fa451399SSean Young 	struct partition *part = container_of(dev, struct partition, mbd);
796e27a9960SSean Young 	int i;
797e27a9960SSean Young 
798e27a9960SSean Young 	for (i=0; i<part->total_blocks; i++) {
799e27a9960SSean Young 		pr_debug("rfd_ftl_remove_dev:'%s': erase unit #%02d: %d erases\n",
800e27a9960SSean Young 			part->mbd.mtd->name, i, part->blocks[i].erases);
801e27a9960SSean Young 	}
802e27a9960SSean Young 
803e27a9960SSean Young 	vfree(part->sector_map);
804e27a9960SSean Young 	kfree(part->header_cache);
805e27a9960SSean Young 	kfree(part->blocks);
806fa451399SSean Young 	del_mtd_blktrans_dev(&part->mbd);
807e27a9960SSean Young }
808e27a9960SSean Young 
8097fe9296cSAdrian Bunk static struct mtd_blktrans_ops rfd_ftl_tr = {
810e27a9960SSean Young 	.name		= "rfd",
811e27a9960SSean Young 	.major		= RFD_FTL_MAJOR,
812e27a9960SSean Young 	.part_bits	= PART_BITS,
81319187672SRichard Purdie 	.blksize 	= SECTOR_SIZE,
81419187672SRichard Purdie 
815e27a9960SSean Young 	.readsect	= rfd_ftl_readsect,
816e27a9960SSean Young 	.writesect	= rfd_ftl_writesect,
817a3a44784SSean Young 	.discard	= rfd_ftl_discardsect,
818e27a9960SSean Young 	.getgeo		= rfd_ftl_getgeo,
819e27a9960SSean Young 	.add_mtd	= rfd_ftl_add_mtd,
820e27a9960SSean Young 	.remove_dev	= rfd_ftl_remove_dev,
821e27a9960SSean Young 	.owner		= THIS_MODULE,
822e27a9960SSean Young };
823e27a9960SSean Young 
824d38c2b93SDejin Zheng module_mtd_blktrans(rfd_ftl_tr);
825e27a9960SSean Young 
826e27a9960SSean Young MODULE_LICENSE("GPL");
827e27a9960SSean Young MODULE_AUTHOR("Sean Young <sean@mess.org>");
828e27a9960SSean Young MODULE_DESCRIPTION("Support code for RFD Flash Translation Layer, "
829e27a9960SSean Young 		"used by General Software's Embedded BIOS");
830e27a9960SSean Young 
831