xref: /linux/drivers/mtd/rfd_ftl.c (revision 662fa3d6099374c4615bf64d06895e3573b935b2)
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 		goto err;
197 
198 	for (i=0; i<part->sector_count; i++)
199 		part->sector_map[i] = -1;
200 
201 	for (i=0, blocks_found=0; i<part->total_blocks; i++) {
202 		rc = mtd_read(part->mbd.mtd, i * part->block_size,
203 			      part->header_size, &retlen,
204 			      (u_char *)part->header_cache);
205 
206 		if (!rc && retlen != part->header_size)
207 			rc = -EIO;
208 
209 		if (rc)
210 			goto err;
211 
212 		if (!build_block_map(part, i))
213 			blocks_found++;
214 	}
215 
216 	if (blocks_found == 0) {
217 		printk(KERN_NOTICE PREFIX "no RFD magic found in '%s'\n",
218 				part->mbd.mtd->name);
219 		rc = -ENOENT;
220 		goto err;
221 	}
222 
223 	if (part->reserved_block == -1) {
224 		printk(KERN_WARNING PREFIX "'%s': no empty erase unit found\n",
225 				part->mbd.mtd->name);
226 
227 		part->errors = 1;
228 	}
229 
230 	return 0;
231 
232 err:
233 	vfree(part->sector_map);
234 	kfree(part->header_cache);
235 	kfree(part->blocks);
236 
237 	return rc;
238 }
239 
240 static int rfd_ftl_readsect(struct mtd_blktrans_dev *dev, u_long sector, char *buf)
241 {
242 	struct partition *part = (struct partition*)dev;
243 	u_long addr;
244 	size_t retlen;
245 	int rc;
246 
247 	if (sector >= part->sector_count)
248 		return -EIO;
249 
250 	addr = part->sector_map[sector];
251 	if (addr != -1) {
252 		rc = mtd_read(part->mbd.mtd, addr, SECTOR_SIZE, &retlen,
253 			      (u_char *)buf);
254 		if (!rc && retlen != SECTOR_SIZE)
255 			rc = -EIO;
256 
257 		if (rc) {
258 			printk(KERN_WARNING PREFIX "error reading '%s' at "
259 				"0x%lx\n", part->mbd.mtd->name, addr);
260 			return rc;
261 		}
262 	} else
263 		memset(buf, 0, SECTOR_SIZE);
264 
265 	return 0;
266 }
267 
268 static int erase_block(struct partition *part, int block)
269 {
270 	struct erase_info *erase;
271 	int rc;
272 
273 	erase = kmalloc(sizeof(struct erase_info), GFP_KERNEL);
274 	if (!erase)
275 		return -ENOMEM;
276 
277 	erase->addr = part->blocks[block].offset;
278 	erase->len = part->block_size;
279 
280 	part->blocks[block].state = BLOCK_ERASING;
281 	part->blocks[block].free_sectors = 0;
282 
283 	rc = mtd_erase(part->mbd.mtd, erase);
284 	if (rc) {
285 		printk(KERN_ERR PREFIX "erase of region %llx,%llx on '%s' "
286 				"failed\n", (unsigned long long)erase->addr,
287 				(unsigned long long)erase->len, part->mbd.mtd->name);
288 		part->blocks[block].state = BLOCK_FAILED;
289 		part->blocks[block].free_sectors = 0;
290 		part->blocks[block].used_sectors = 0;
291 	} else {
292 		u16 magic = cpu_to_le16(RFD_MAGIC);
293 		size_t retlen;
294 
295 		part->blocks[block].state = BLOCK_ERASED;
296 		part->blocks[block].free_sectors = part->data_sectors_per_block;
297 		part->blocks[block].used_sectors = 0;
298 		part->blocks[block].erases++;
299 
300 		rc = mtd_write(part->mbd.mtd, part->blocks[block].offset,
301 			       sizeof(magic), &retlen, (u_char *)&magic);
302 		if (!rc && retlen != sizeof(magic))
303 			rc = -EIO;
304 
305 		if (rc) {
306 			pr_err(PREFIX "'%s': unable to write RFD header at 0x%lx\n",
307 			       part->mbd.mtd->name, part->blocks[block].offset);
308 			part->blocks[block].state = BLOCK_FAILED;
309 		} else {
310 			part->blocks[block].state = BLOCK_OK;
311 		}
312 	}
313 
314 	kfree(erase);
315 
316 	return rc;
317 }
318 
319 static int move_block_contents(struct partition *part, int block_no, u_long *old_sector)
320 {
321 	void *sector_data;
322 	u16 *map;
323 	size_t retlen;
324 	int i, rc = -ENOMEM;
325 
326 	part->is_reclaiming = 1;
327 
328 	sector_data = kmalloc(SECTOR_SIZE, GFP_KERNEL);
329 	if (!sector_data)
330 		goto err3;
331 
332 	map = kmalloc(part->header_size, GFP_KERNEL);
333 	if (!map)
334 		goto err2;
335 
336 	rc = mtd_read(part->mbd.mtd, part->blocks[block_no].offset,
337 		      part->header_size, &retlen, (u_char *)map);
338 
339 	if (!rc && retlen != part->header_size)
340 		rc = -EIO;
341 
342 	if (rc) {
343 		printk(KERN_ERR PREFIX "error reading '%s' at "
344 			"0x%lx\n", part->mbd.mtd->name,
345 			part->blocks[block_no].offset);
346 
347 		goto err;
348 	}
349 
350 	for (i=0; i<part->data_sectors_per_block; i++) {
351 		u16 entry = le16_to_cpu(map[HEADER_MAP_OFFSET + i]);
352 		u_long addr;
353 
354 
355 		if (entry == SECTOR_FREE || entry == SECTOR_DELETED)
356 			continue;
357 
358 		if (entry == SECTOR_ZERO)
359 			entry = 0;
360 
361 		/* already warned about and ignored in build_block_map() */
362 		if (entry >= part->sector_count)
363 			continue;
364 
365 		addr = part->blocks[block_no].offset +
366 			(i + part->header_sectors_per_block) * SECTOR_SIZE;
367 
368 		if (*old_sector == addr) {
369 			*old_sector = -1;
370 			if (!part->blocks[block_no].used_sectors--) {
371 				rc = erase_block(part, block_no);
372 				break;
373 			}
374 			continue;
375 		}
376 		rc = mtd_read(part->mbd.mtd, addr, SECTOR_SIZE, &retlen,
377 			      sector_data);
378 
379 		if (!rc && retlen != SECTOR_SIZE)
380 			rc = -EIO;
381 
382 		if (rc) {
383 			printk(KERN_ERR PREFIX "'%s': Unable to "
384 				"read sector for relocation\n",
385 				part->mbd.mtd->name);
386 
387 			goto err;
388 		}
389 
390 		rc = rfd_ftl_writesect((struct mtd_blktrans_dev*)part,
391 				entry, sector_data);
392 
393 		if (rc)
394 			goto err;
395 	}
396 
397 err:
398 	kfree(map);
399 err2:
400 	kfree(sector_data);
401 err3:
402 	part->is_reclaiming = 0;
403 
404 	return rc;
405 }
406 
407 static int reclaim_block(struct partition *part, u_long *old_sector)
408 {
409 	int block, best_block, score, old_sector_block;
410 	int rc;
411 
412 	/* we have a race if sync doesn't exist */
413 	mtd_sync(part->mbd.mtd);
414 
415 	score = 0x7fffffff; /* MAX_INT */
416 	best_block = -1;
417 	if (*old_sector != -1)
418 		old_sector_block = *old_sector / part->block_size;
419 	else
420 		old_sector_block = -1;
421 
422 	for (block=0; block<part->total_blocks; block++) {
423 		int this_score;
424 
425 		if (block == part->reserved_block)
426 			continue;
427 
428 		/*
429 		 * Postpone reclaiming if there is a free sector as
430 		 * more removed sectors is more efficient (have to move
431 		 * less).
432 		 */
433 		if (part->blocks[block].free_sectors)
434 			return 0;
435 
436 		this_score = part->blocks[block].used_sectors;
437 
438 		if (block == old_sector_block)
439 			this_score--;
440 		else {
441 			/* no point in moving a full block */
442 			if (part->blocks[block].used_sectors ==
443 					part->data_sectors_per_block)
444 				continue;
445 		}
446 
447 		this_score += part->blocks[block].erases;
448 
449 		if (this_score < score) {
450 			best_block = block;
451 			score = this_score;
452 		}
453 	}
454 
455 	if (best_block == -1)
456 		return -ENOSPC;
457 
458 	part->current_block = -1;
459 	part->reserved_block = best_block;
460 
461 	pr_debug("reclaim_block: reclaiming block #%d with %d used "
462 		 "%d free sectors\n", best_block,
463 		 part->blocks[best_block].used_sectors,
464 		 part->blocks[best_block].free_sectors);
465 
466 	if (part->blocks[best_block].used_sectors)
467 		rc = move_block_contents(part, best_block, old_sector);
468 	else
469 		rc = erase_block(part, best_block);
470 
471 	return rc;
472 }
473 
474 /*
475  * IMPROVE: It would be best to choose the block with the most deleted sectors,
476  * because if we fill that one up first it'll have the most chance of having
477  * the least live sectors at reclaim.
478  */
479 static int find_free_block(struct partition *part)
480 {
481 	int block, stop;
482 
483 	block = part->current_block == -1 ?
484 			jiffies % part->total_blocks : part->current_block;
485 	stop = block;
486 
487 	do {
488 		if (part->blocks[block].free_sectors &&
489 				block != part->reserved_block)
490 			return block;
491 
492 		if (part->blocks[block].state == BLOCK_UNUSED)
493 			erase_block(part, block);
494 
495 		if (++block >= part->total_blocks)
496 			block = 0;
497 
498 	} while (block != stop);
499 
500 	return -1;
501 }
502 
503 static int find_writable_block(struct partition *part, u_long *old_sector)
504 {
505 	int rc, block;
506 	size_t retlen;
507 
508 	block = find_free_block(part);
509 
510 	if (block == -1) {
511 		if (!part->is_reclaiming) {
512 			rc = reclaim_block(part, old_sector);
513 			if (rc)
514 				goto err;
515 
516 			block = find_free_block(part);
517 		}
518 
519 		if (block == -1) {
520 			rc = -ENOSPC;
521 			goto err;
522 		}
523 	}
524 
525 	rc = mtd_read(part->mbd.mtd, part->blocks[block].offset,
526 		      part->header_size, &retlen,
527 		      (u_char *)part->header_cache);
528 
529 	if (!rc && retlen != part->header_size)
530 		rc = -EIO;
531 
532 	if (rc) {
533 		printk(KERN_ERR PREFIX "'%s': unable to read header at "
534 				"0x%lx\n", part->mbd.mtd->name,
535 				part->blocks[block].offset);
536 		goto err;
537 	}
538 
539 	part->current_block = block;
540 
541 err:
542 	return rc;
543 }
544 
545 static int mark_sector_deleted(struct partition *part, u_long old_addr)
546 {
547 	int block, offset, rc;
548 	u_long addr;
549 	size_t retlen;
550 	u16 del = cpu_to_le16(SECTOR_DELETED);
551 
552 	block = old_addr / part->block_size;
553 	offset = (old_addr % part->block_size) / SECTOR_SIZE -
554 		part->header_sectors_per_block;
555 
556 	addr = part->blocks[block].offset +
557 			(HEADER_MAP_OFFSET + offset) * sizeof(u16);
558 	rc = mtd_write(part->mbd.mtd, addr, sizeof(del), &retlen,
559 		       (u_char *)&del);
560 
561 	if (!rc && retlen != sizeof(del))
562 		rc = -EIO;
563 
564 	if (rc) {
565 		printk(KERN_ERR PREFIX "error writing '%s' at "
566 			"0x%lx\n", part->mbd.mtd->name, addr);
567 		goto err;
568 	}
569 	if (block == part->current_block)
570 		part->header_cache[offset + HEADER_MAP_OFFSET] = del;
571 
572 	part->blocks[block].used_sectors--;
573 
574 	if (!part->blocks[block].used_sectors &&
575 	    !part->blocks[block].free_sectors)
576 		rc = erase_block(part, block);
577 
578 err:
579 	return rc;
580 }
581 
582 static int find_free_sector(const struct partition *part, const struct block *block)
583 {
584 	int i, stop;
585 
586 	i = stop = part->data_sectors_per_block - block->free_sectors;
587 
588 	do {
589 		if (le16_to_cpu(part->header_cache[HEADER_MAP_OFFSET + i])
590 				== SECTOR_FREE)
591 			return i;
592 
593 		if (++i == part->data_sectors_per_block)
594 			i = 0;
595 	}
596 	while(i != stop);
597 
598 	return -1;
599 }
600 
601 static int do_writesect(struct mtd_blktrans_dev *dev, u_long sector, char *buf, ulong *old_addr)
602 {
603 	struct partition *part = (struct partition*)dev;
604 	struct block *block;
605 	u_long addr;
606 	int i;
607 	int rc;
608 	size_t retlen;
609 	u16 entry;
610 
611 	if (part->current_block == -1 ||
612 		!part->blocks[part->current_block].free_sectors) {
613 
614 		rc = find_writable_block(part, old_addr);
615 		if (rc)
616 			goto err;
617 	}
618 
619 	block = &part->blocks[part->current_block];
620 
621 	i = find_free_sector(part, block);
622 
623 	if (i < 0) {
624 		rc = -ENOSPC;
625 		goto err;
626 	}
627 
628 	addr = (i + part->header_sectors_per_block) * SECTOR_SIZE +
629 		block->offset;
630 	rc = mtd_write(part->mbd.mtd, addr, SECTOR_SIZE, &retlen,
631 		       (u_char *)buf);
632 
633 	if (!rc && retlen != SECTOR_SIZE)
634 		rc = -EIO;
635 
636 	if (rc) {
637 		printk(KERN_ERR PREFIX "error writing '%s' at 0x%lx\n",
638 				part->mbd.mtd->name, addr);
639 		goto err;
640 	}
641 
642 	part->sector_map[sector] = addr;
643 
644 	entry = cpu_to_le16(sector == 0 ? SECTOR_ZERO : sector);
645 
646 	part->header_cache[i + HEADER_MAP_OFFSET] = entry;
647 
648 	addr = block->offset + (HEADER_MAP_OFFSET + i) * sizeof(u16);
649 	rc = mtd_write(part->mbd.mtd, addr, sizeof(entry), &retlen,
650 		       (u_char *)&entry);
651 
652 	if (!rc && retlen != sizeof(entry))
653 		rc = -EIO;
654 
655 	if (rc) {
656 		printk(KERN_ERR PREFIX "error writing '%s' at 0x%lx\n",
657 				part->mbd.mtd->name, addr);
658 		goto err;
659 	}
660 	block->used_sectors++;
661 	block->free_sectors--;
662 
663 err:
664 	return rc;
665 }
666 
667 static int rfd_ftl_writesect(struct mtd_blktrans_dev *dev, u_long sector, char *buf)
668 {
669 	struct partition *part = (struct partition*)dev;
670 	u_long old_addr;
671 	int i;
672 	int rc = 0;
673 
674 	pr_debug("rfd_ftl_writesect(sector=0x%lx)\n", sector);
675 
676 	if (part->reserved_block == -1) {
677 		rc = -EACCES;
678 		goto err;
679 	}
680 
681 	if (sector >= part->sector_count) {
682 		rc = -EIO;
683 		goto err;
684 	}
685 
686 	old_addr = part->sector_map[sector];
687 
688 	for (i=0; i<SECTOR_SIZE; i++) {
689 		if (!buf[i])
690 			continue;
691 
692 		rc = do_writesect(dev, sector, buf, &old_addr);
693 		if (rc)
694 			goto err;
695 		break;
696 	}
697 
698 	if (i == SECTOR_SIZE)
699 		part->sector_map[sector] = -1;
700 
701 	if (old_addr != -1)
702 		rc = mark_sector_deleted(part, old_addr);
703 
704 err:
705 	return rc;
706 }
707 
708 static int rfd_ftl_getgeo(struct mtd_blktrans_dev *dev, struct hd_geometry *geo)
709 {
710 	struct partition *part = (struct partition*)dev;
711 
712 	geo->heads = 1;
713 	geo->sectors = SECTORS_PER_TRACK;
714 	geo->cylinders = part->cylinders;
715 
716 	return 0;
717 }
718 
719 static void rfd_ftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
720 {
721 	struct partition *part;
722 
723 	if (mtd->type != MTD_NORFLASH || mtd->size > UINT_MAX)
724 		return;
725 
726 	part = kzalloc(sizeof(struct partition), GFP_KERNEL);
727 	if (!part)
728 		return;
729 
730 	part->mbd.mtd = mtd;
731 
732 	if (block_size)
733 		part->block_size = block_size;
734 	else {
735 		if (!mtd->erasesize) {
736 			printk(KERN_WARNING PREFIX "please provide block_size");
737 			goto out;
738 		} else
739 			part->block_size = mtd->erasesize;
740 	}
741 
742 	if (scan_header(part) == 0) {
743 		part->mbd.size = part->sector_count;
744 		part->mbd.tr = tr;
745 		part->mbd.devnum = -1;
746 		if (!(mtd->flags & MTD_WRITEABLE))
747 			part->mbd.readonly = 1;
748 		else if (part->errors) {
749 			printk(KERN_WARNING PREFIX "'%s': errors found, "
750 					"setting read-only\n", mtd->name);
751 			part->mbd.readonly = 1;
752 		}
753 
754 		printk(KERN_INFO PREFIX "name: '%s' type: %d flags %x\n",
755 				mtd->name, mtd->type, mtd->flags);
756 
757 		if (!add_mtd_blktrans_dev((void*)part))
758 			return;
759 	}
760 out:
761 	kfree(part);
762 }
763 
764 static void rfd_ftl_remove_dev(struct mtd_blktrans_dev *dev)
765 {
766 	struct partition *part = (struct partition*)dev;
767 	int i;
768 
769 	for (i=0; i<part->total_blocks; i++) {
770 		pr_debug("rfd_ftl_remove_dev:'%s': erase unit #%02d: %d erases\n",
771 			part->mbd.mtd->name, i, part->blocks[i].erases);
772 	}
773 
774 	del_mtd_blktrans_dev(dev);
775 	vfree(part->sector_map);
776 	kfree(part->header_cache);
777 	kfree(part->blocks);
778 }
779 
780 static struct mtd_blktrans_ops rfd_ftl_tr = {
781 	.name		= "rfd",
782 	.major		= RFD_FTL_MAJOR,
783 	.part_bits	= PART_BITS,
784 	.blksize 	= SECTOR_SIZE,
785 
786 	.readsect	= rfd_ftl_readsect,
787 	.writesect	= rfd_ftl_writesect,
788 	.getgeo		= rfd_ftl_getgeo,
789 	.add_mtd	= rfd_ftl_add_mtd,
790 	.remove_dev	= rfd_ftl_remove_dev,
791 	.owner		= THIS_MODULE,
792 };
793 
794 module_mtd_blktrans(rfd_ftl_tr);
795 
796 MODULE_LICENSE("GPL");
797 MODULE_AUTHOR("Sean Young <sean@mess.org>");
798 MODULE_DESCRIPTION("Support code for RFD Flash Translation Layer, "
799 		"used by General Software's Embedded BIOS");
800 
801