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