aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mtd
diff options
context:
space:
mode:
authorSean Young <sean@mess.org>2005-06-16 04:49:33 -0400
committerThomas Gleixner <tglx@mtd.linutronix.de>2005-11-06 14:08:54 -0500
commite27a9960af0506d84b9ca9dd3874b7d88901f230 (patch)
treecac8c7876c3917c301e663d82b3877aa7f9935ab /drivers/mtd
parent45ca1b509ea156e87c99e529821fb3b548e14fe3 (diff)
[MTD] Add Resident Flash Disk (RFD) support
This type of flash translation layer (FTL) is used by the Embedded BIOS by General Software. It is known as the Resident Flash Disk (RFD), see: http://www.gensw.com/pages/prod/bios/rfd.htm Signed-off-by: Sean Young <sean@mess.org> Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Diffstat (limited to 'drivers/mtd')
-rw-r--r--drivers/mtd/Kconfig12
-rw-r--r--drivers/mtd/Makefile3
-rw-r--r--drivers/mtd/maps/Kconfig9
-rw-r--r--drivers/mtd/maps/ts5500_flash.c6
-rw-r--r--drivers/mtd/rfd_ftl.c855
5 files changed, 873 insertions, 12 deletions
diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig
index 027054dea032..843a1cbe0866 100644
--- a/drivers/mtd/Kconfig
+++ b/drivers/mtd/Kconfig
@@ -1,4 +1,4 @@
1# $Id: Kconfig,v 1.7 2004/11/22 11:33:56 ijc Exp $ 1# $Id: Kconfig,v 1.9 2005/06/16 08:49:29 sean Exp $
2 2
3menu "Memory Technology Devices (MTD)" 3menu "Memory Technology Devices (MTD)"
4 4
@@ -253,6 +253,16 @@ config INFTL
253 permitted to copy, modify and distribute the code as you wish. Just 253 permitted to copy, modify and distribute the code as you wish. Just
254 not use it. 254 not use it.
255 255
256config RFD_FTL
257 tristate "Resident Flash Disk (Flash Translation Layer) support"
258 depends on MTD
259 ---help---
260 This provides support for the flash translation layer known
261 as the Resident Flash Disk (RFD), as used by the Embedded BIOS
262 of General Software.
263 See http://www.gensw.com/pages/prod/bios/rfd.htm for further
264 information.
265
256source "drivers/mtd/chips/Kconfig" 266source "drivers/mtd/chips/Kconfig"
257 267
258source "drivers/mtd/maps/Kconfig" 268source "drivers/mtd/maps/Kconfig"
diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile
index e4ad588327f7..cb16b7d478ce 100644
--- a/drivers/mtd/Makefile
+++ b/drivers/mtd/Makefile
@@ -1,7 +1,7 @@
1# 1#
2# Makefile for the memory technology device drivers. 2# Makefile for the memory technology device drivers.
3# 3#
4# $Id: Makefile.common,v 1.5 2004/08/10 20:51:49 dwmw2 Exp $ 4# $Id: Makefile.common,v 1.6 2005/06/16 08:49:29 sean Exp $
5 5
6# Core functionality. 6# Core functionality.
7mtd-y := mtdcore.o 7mtd-y := mtdcore.o
@@ -20,6 +20,7 @@ obj-$(CONFIG_MTD_BLOCK_RO) += mtdblock_ro.o mtd_blkdevs.o
20obj-$(CONFIG_FTL) += ftl.o mtd_blkdevs.o 20obj-$(CONFIG_FTL) += ftl.o mtd_blkdevs.o
21obj-$(CONFIG_NFTL) += nftl.o mtd_blkdevs.o 21obj-$(CONFIG_NFTL) += nftl.o mtd_blkdevs.o
22obj-$(CONFIG_INFTL) += inftl.o mtd_blkdevs.o 22obj-$(CONFIG_INFTL) += inftl.o mtd_blkdevs.o
23obj-$(CONFIG_RFD_FTL) += rfd_ftl.o mtd_blkdevs.o
23 24
24nftl-objs := nftlcore.o nftlmount.o 25nftl-objs := nftlcore.o nftlmount.o
25inftl-objs := inftlcore.o inftlmount.o 26inftl-objs := inftlcore.o inftlmount.o
diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig
index 44781a83b2e7..aa5d71f8d812 100644
--- a/drivers/mtd/maps/Kconfig
+++ b/drivers/mtd/maps/Kconfig
@@ -97,14 +97,11 @@ config MTD_TS5500
97 depends on X86 && MTD_JEDECPROBE && MTD_PARTITIONS 97 depends on X86 && MTD_JEDECPROBE && MTD_PARTITIONS
98 help 98 help
99 This provides a driver for the on-board flash of the Technologic 99 This provides a driver for the on-board flash of the Technologic
100 System's TS-5500 board. The flash is split into 3 partitions 100 System's TS-5500 board. The 2MB flash is split into 3 partitions
101 which are accessed as separate MTD devices. 101 which are accessed as separate MTD devices.
102 102
103 mtd0 and mtd2 are the two BIOS drives. Unfortunately the BIOS 103 mtd0 and mtd2 are the two BIOS drives, which use the resident
104 uses a proprietary flash translation layer from General Software, 104 flash disk (RFD) flash translation layer.
105 which is not supported (the drives cannot be mounted). You can
106 create your own file system (jffs for example), but the BIOS
107 won't be able to boot from it.
108 105
109 mtd1 allows you to reprogram your BIOS. BE VERY CAREFUL. 106 mtd1 allows you to reprogram your BIOS. BE VERY CAREFUL.
110 107
diff --git a/drivers/mtd/maps/ts5500_flash.c b/drivers/mtd/maps/ts5500_flash.c
index 3ebd90f56503..286dd82e8b83 100644
--- a/drivers/mtd/maps/ts5500_flash.c
+++ b/drivers/mtd/maps/ts5500_flash.c
@@ -19,13 +19,11 @@
19 * 19 *
20 * Note: 20 * Note:
21 * - In order for detection to work, jumper 3 must be set. 21 * - In order for detection to work, jumper 3 must be set.
22 * - Drive A and B use a proprietary FTL from General Software which isn't 22 * - Drive A and B use the resident flash disk (RFD) flash translation layer.
23 * supported as of yet so standard drives can't be mounted; you can create
24 * your own (e.g. jffs) file system.
25 * - If you have created your own jffs file system and the bios overwrites 23 * - If you have created your own jffs file system and the bios overwrites
26 * it during boot, try disabling Drive A: and B: in the boot order. 24 * it during boot, try disabling Drive A: and B: in the boot order.
27 * 25 *
28 * $Id: ts5500_flash.c,v 1.2 2004/11/28 09:40:40 dwmw2 Exp $ 26 * $Id: ts5500_flash.c,v 1.3 2005/06/16 08:49:30 sean Exp $
29 */ 27 */
30 28
31#include <linux/config.h> 29#include <linux/config.h>
diff --git a/drivers/mtd/rfd_ftl.c b/drivers/mtd/rfd_ftl.c
new file mode 100644
index 000000000000..7b9f359f04b7
--- /dev/null
+++ b/drivers/mtd/rfd_ftl.c
@@ -0,0 +1,855 @@
1/*
2 * rfd_ftl.c -- resident flash disk (flash translation layer)
3 *
4 * Copyright (C) 2005 Sean Young <sean@mess.org>
5 *
6 * $Id: rfd_ftl.c,v 1.4 2005/07/31 22:49:14 sean Exp $
7 *
8 * This type of flash translation layer (FTL) is used by the Embedded BIOS
9 * by General Software. It is known as the Resident Flash Disk (RFD), see:
10 *
11 * http://www.gensw.com/pages/prod/bios/rfd.htm
12 *
13 * based on ftl.c
14 */
15
16#include <linux/hdreg.h>
17#include <linux/init.h>
18#include <linux/mtd/blktrans.h>
19#include <linux/mtd/mtd.h>
20#include <linux/vmalloc.h>
21
22#include <asm/types.h>
23
24#define const_cpu_to_le16 __constant_cpu_to_le16
25
26static int block_size = 0;
27module_param(block_size, int, 0);
28MODULE_PARM_DESC(block_size, "Block size to use by RFD, defaults to erase unit size");
29
30#define PREFIX "rfd_ftl: "
31
32/* Major device # for FTL device */
33
34/* A request for this major has been sent to device@lanana.org */
35#ifndef RFD_FTL_MAJOR
36#define RFD_FTL_MAJOR 95
37#endif
38
39/* Maximum number of partitions in an FTL region */
40#define PART_BITS 4
41
42/* An erase unit should start with this value */
43#define RFD_MAGIC 0x9193
44
45/* the second value is 0xffff or 0xffc8; function unknown */
46
47/* the third value is always 0xffff, ignored */
48
49/* next is an array of mapping for each corresponding sector */
50#define HEADER_MAP_OFFSET 3
51#define SECTOR_DELETED 0x0000
52#define SECTOR_ZERO 0xfffe
53#define SECTOR_FREE 0xffff
54
55#define SECTOR_SIZE 512
56
57#define SECTORS_PER_TRACK 63
58
59struct block {
60 enum {
61 BLOCK_OK,
62 BLOCK_ERASING,
63 BLOCK_ERASED,
64 BLOCK_FAILED
65 } state;
66 int free_sectors;
67 int used_sectors;
68 int erases;
69 u_long offset;
70};
71
72struct partition {
73 struct mtd_blktrans_dev mbd;
74
75 u_int block_size; /* size of erase unit */
76 u_int total_blocks; /* number of erase units */
77 u_int header_sectors_per_block; /* header sectors in erase unit */
78 u_int data_sectors_per_block; /* data sectors in erase unit */
79 u_int sector_count; /* sectors in translated disk */
80 u_int header_size; /* bytes in header sector */
81 int reserved_block; /* block next up for reclaim */
82 int current_block; /* block to write to */
83 u16 *header_cache; /* cached header */
84
85 int is_reclaiming;
86 int cylinders;
87 int errors;
88 u_long *sector_map;
89 struct block *blocks;
90};
91
92static int rfd_ftl_writesect(struct mtd_blktrans_dev *dev, u_long sector, char *buf);
93
94static int build_block_map(struct partition *part, int block_no)
95{
96 struct block *block = &part->blocks[block_no];
97 int i;
98
99 block->offset = part->block_size * block_no;
100
101 if (le16_to_cpu(part->header_cache[0]) != RFD_MAGIC) {
102 block->state = BLOCK_ERASED; /* assumption */
103 block->free_sectors = part->data_sectors_per_block;
104 part->reserved_block = block_no;
105 return 1;
106 }
107
108 block->state = BLOCK_OK;
109
110 for (i=0; i<part->data_sectors_per_block; i++) {
111 u16 entry;
112
113 entry = le16_to_cpu(part->header_cache[HEADER_MAP_OFFSET + i]);
114
115 if (entry == SECTOR_DELETED)
116 continue;
117
118 if (entry == SECTOR_FREE) {
119 block->free_sectors++;
120 continue;
121 }
122
123 if (entry == SECTOR_ZERO)
124 entry = 0;
125
126 if (entry >= part->sector_count) {
127 printk(KERN_NOTICE PREFIX
128 "'%s': unit #%d: entry %d corrupt, "
129 "sector %d out of range\n",
130 part->mbd.mtd->name, block_no, i, entry);
131 continue;
132 }
133
134 if (part->sector_map[entry] != -1) {
135 printk(KERN_NOTICE PREFIX
136 "'%s': more than one entry for sector %d\n",
137 part->mbd.mtd->name, entry);
138 part->errors = 1;
139 continue;
140 }
141
142 part->sector_map[entry] = block->offset +
143 (i + part->header_sectors_per_block) * SECTOR_SIZE;
144
145 block->used_sectors++;
146 }
147
148 if (block->free_sectors == part->data_sectors_per_block)
149 part->reserved_block = block_no;
150
151 return 0;
152}
153
154static int scan_header(struct partition *part)
155{
156 int sectors_per_block;
157 int i, rc = -ENOMEM;
158 int blocks_found;
159 size_t retlen;
160
161 sectors_per_block = part->block_size / SECTOR_SIZE;
162 part->total_blocks = part->mbd.mtd->size / part->block_size;
163
164 if (part->total_blocks < 2)
165 return -ENOENT;
166
167 /* each erase block has three bytes header, followed by the map */
168 part->header_sectors_per_block =
169 ((HEADER_MAP_OFFSET + sectors_per_block) *
170 sizeof(u16) + SECTOR_SIZE - 1) / SECTOR_SIZE;
171
172 part->data_sectors_per_block = sectors_per_block -
173 part->header_sectors_per_block;
174
175 part->header_size = (HEADER_MAP_OFFSET +
176 part->data_sectors_per_block) * sizeof(u16);
177
178 part->cylinders = (part->data_sectors_per_block *
179 (part->total_blocks - 1) - 1) / SECTORS_PER_TRACK;
180
181 part->sector_count = part->cylinders * SECTORS_PER_TRACK;
182
183 part->current_block = -1;
184 part->reserved_block = -1;
185 part->is_reclaiming = 0;
186
187 part->header_cache = kmalloc(part->header_size, GFP_KERNEL);
188 if (!part->header_cache)
189 goto err;
190
191 part->blocks = kcalloc(part->total_blocks, sizeof(struct block),
192 GFP_KERNEL);
193 if (!part->blocks)
194 goto err;
195
196 part->sector_map = vmalloc(part->sector_count * sizeof(u_long));
197 if (!part->sector_map) {
198 printk(KERN_ERR PREFIX "'%s': unable to allocate memory for "
199 "sector map", part->mbd.mtd->name);
200 goto err;
201 }
202
203 for (i=0; i<part->sector_count; i++)
204 part->sector_map[i] = -1;
205
206 for (i=0, blocks_found=0; i<part->total_blocks; i++) {
207 rc = part->mbd.mtd->read(part->mbd.mtd,
208 i * part->block_size, part->header_size,
209 &retlen, (u_char*)part->header_cache);
210
211 if (!rc && retlen != part->header_size)
212 rc = -EIO;
213
214 if (rc)
215 goto err;
216
217 if (!build_block_map(part, i))
218 blocks_found++;
219 }
220
221 if (blocks_found == 0) {
222 printk(KERN_NOTICE PREFIX "no RFD magic found in '%s'\n",
223 part->mbd.mtd->name);
224 rc = -ENOENT;
225 goto err;
226 }
227
228 if (part->reserved_block == -1) {
229 printk(KERN_NOTICE PREFIX "'%s': no empty erase unit found\n",
230 part->mbd.mtd->name);
231
232 part->errors = 1;
233 }
234
235 return 0;
236
237err:
238 vfree(part->sector_map);
239 kfree(part->header_cache);
240 kfree(part->blocks);
241
242 return rc;
243}
244
245static int rfd_ftl_readsect(struct mtd_blktrans_dev *dev, u_long sector, char *buf)
246{
247 struct partition *part = (struct partition*)dev;
248 u_long addr;
249 size_t retlen;
250 int rc;
251
252 if (sector >= part->sector_count)
253 return -EIO;
254
255 addr = part->sector_map[sector];
256 if (addr != -1) {
257 rc = part->mbd.mtd->read(part->mbd.mtd, addr, SECTOR_SIZE,
258 &retlen, (u_char*)buf);
259 if (!rc && retlen != SECTOR_SIZE)
260 rc = -EIO;
261
262 if (rc) {
263 printk(KERN_WARNING PREFIX "error reading '%s' at "
264 "0x%lx\n", part->mbd.mtd->name, addr);
265 return rc;
266 }
267 } else
268 memset(buf, 0, SECTOR_SIZE);
269
270 return 0;
271}
272
273static void erase_callback(struct erase_info *erase)
274{
275 struct partition *part;
276 u16 magic;
277 int i, rc;
278 size_t retlen;
279
280 part = (struct partition*)erase->priv;
281
282 i = erase->addr / part->block_size;
283 if (i >= part->total_blocks || part->blocks[i].offset != erase->addr) {
284 printk(KERN_ERR PREFIX "erase callback for unknown offset %x "
285 "on '%s'\n", erase->addr, part->mbd.mtd->name);
286 return;
287 }
288
289 if (erase->state != MTD_ERASE_DONE) {
290 printk(KERN_WARNING PREFIX "erase failed at 0x%x on '%s', "
291 "state %d\n", erase->addr,
292 part->mbd.mtd->name, erase->state);
293
294 part->blocks[i].state = BLOCK_FAILED;
295 part->blocks[i].free_sectors = 0;
296 part->blocks[i].used_sectors = 0;
297
298 kfree(erase);
299
300 return;
301 }
302
303 magic = const_cpu_to_le16(RFD_MAGIC);
304
305 part->blocks[i].state = BLOCK_ERASED;
306 part->blocks[i].free_sectors = part->data_sectors_per_block;
307 part->blocks[i].used_sectors = 0;
308 part->blocks[i].erases++;
309
310 rc = part->mbd.mtd->write(part->mbd.mtd,
311 part->blocks[i].offset, sizeof(magic), &retlen,
312 (u_char*)&magic);
313
314 if (!rc && retlen != sizeof(magic))
315 rc = -EIO;
316
317 if (rc) {
318 printk(KERN_NOTICE PREFIX "'%s': unable to write RFD "
319 "header at 0x%lx\n",
320 part->mbd.mtd->name,
321 part->blocks[i].offset);
322 part->blocks[i].state = BLOCK_FAILED;
323 }
324 else
325 part->blocks[i].state = BLOCK_OK;
326
327 kfree(erase);
328}
329
330static int erase_block(struct partition *part, int block)
331{
332 struct erase_info *erase;
333 int rc = -ENOMEM;
334
335 erase = kmalloc(sizeof(struct erase_info), GFP_KERNEL);
336 if (!erase)
337 goto err;
338
339 erase->mtd = part->mbd.mtd;
340 erase->callback = erase_callback;
341 erase->addr = part->blocks[block].offset;
342 erase->len = part->block_size;
343 erase->priv = (u_long)part;
344
345 part->blocks[block].state = BLOCK_ERASING;
346 part->blocks[block].free_sectors = 0;
347
348 rc = part->mbd.mtd->erase(part->mbd.mtd, erase);
349
350 if (rc) {
351 printk(KERN_WARNING PREFIX "erase of region %x,%x on '%s' "
352 "failed\n", erase->addr, erase->len,
353 part->mbd.mtd->name);
354 kfree(erase);
355 }
356
357err:
358 return rc;
359}
360
361static int move_block_contents(struct partition *part, int block_no, u_long *old_sector)
362{
363 void *sector_data;
364 u16 *map;
365 size_t retlen;
366 int i, rc = -ENOMEM;
367
368 part->is_reclaiming = 1;
369
370 sector_data = kmalloc(SECTOR_SIZE, GFP_KERNEL);
371 if (!sector_data)
372 goto err3;
373
374 map = kmalloc(part->header_size, GFP_KERNEL);
375 if (!map)
376 goto err2;
377
378 rc = part->mbd.mtd->read(part->mbd.mtd,
379 part->blocks[block_no].offset, part->header_size,
380 &retlen, (u_char*)map);
381
382 if (!rc && retlen != part->header_size)
383 rc = -EIO;
384
385 if (rc) {
386 printk(KERN_NOTICE PREFIX "error reading '%s' at "
387 "0x%lx\n", part->mbd.mtd->name,
388 part->blocks[block_no].offset);
389
390 goto err;
391 }
392
393 for (i=0; i<part->data_sectors_per_block; i++) {
394 u16 entry = le16_to_cpu(map[HEADER_MAP_OFFSET + i]);
395 u_long addr;
396
397
398 if (entry == SECTOR_FREE || entry == SECTOR_DELETED)
399 continue;
400
401 if (entry == SECTOR_ZERO)
402 entry = 0;
403
404 /* already warned about and ignored in build_block_map() */
405 if (entry >= part->sector_count)
406 continue;
407
408 addr = part->blocks[block_no].offset +
409 (i + part->header_sectors_per_block) * SECTOR_SIZE;
410
411 if (*old_sector == addr) {
412 *old_sector = -1;
413 if (!part->blocks[block_no].used_sectors--) {
414 rc = erase_block(part, block_no);
415 break;
416 }
417 continue;
418 }
419 rc = part->mbd.mtd->read(part->mbd.mtd, addr,
420 SECTOR_SIZE, &retlen, sector_data);
421
422 if (!rc && retlen != SECTOR_SIZE)
423 rc = -EIO;
424
425 if (rc) {
426 printk(KERN_NOTICE PREFIX "'%s': Unable to "
427 "read sector for relocation\n",
428 part->mbd.mtd->name);
429
430 goto err;
431 }
432
433 rc = rfd_ftl_writesect((struct mtd_blktrans_dev*)part,
434 entry, sector_data);
435
436 if (rc)
437 goto err;
438 }
439
440err:
441 kfree(map);
442err2:
443 kfree(sector_data);
444err3:
445 part->is_reclaiming = 0;
446
447 return rc;
448}
449
450static int reclaim_block(struct partition *part, u_long *old_sector)
451{
452 int block, best_block, score, old_sector_block;
453 int rc;
454
455 /* we have a race if sync doesn't exist */
456 if (part->mbd.mtd->sync)
457 part->mbd.mtd->sync(part->mbd.mtd);
458
459 score = 0x7fffffff; /* MAX_INT */
460 best_block = -1;
461 if (*old_sector != -1)
462 old_sector_block = *old_sector / part->block_size;
463 else
464 old_sector_block = -1;
465
466 for (block=0; block<part->total_blocks; block++) {
467 int this_score;
468
469 if (block == part->reserved_block)
470 continue;
471
472 /*
473 * Postpone reclaiming if there is a free sector as
474 * more removed sectors is more efficient (have to move
475 * less).
476 */
477 if (part->blocks[block].free_sectors)
478 return 0;
479
480 this_score = part->blocks[block].used_sectors;
481
482 if (block == old_sector_block)
483 this_score--;
484 else {
485 /* no point in moving a full block */
486 if (part->blocks[block].used_sectors ==
487 part->data_sectors_per_block)
488 continue;
489 }
490
491 this_score += part->blocks[block].erases;
492
493 if (this_score < score) {
494 best_block = block;
495 score = this_score;
496 }
497 }
498
499 if (best_block == -1)
500 return -ENOSPC;
501
502 part->current_block = -1;
503 part->reserved_block = best_block;
504
505 pr_debug("reclaim_block: reclaiming block #%d with %d used "
506 "%d free sectors\n", best_block,
507 part->blocks[best_block].used_sectors,
508 part->blocks[best_block].free_sectors);
509
510 if (part->blocks[best_block].used_sectors)
511 rc = move_block_contents(part, best_block, old_sector);
512 else
513 rc = erase_block(part, best_block);
514
515 return rc;
516}
517
518/*
519 * IMPROVE: It would be best to choose the block with the most deleted sectors,
520 * because if we fill that one up first it'll have the most chance of having
521 * the least live sectors at reclaim.
522 */
523static int find_free_block(const struct partition *part)
524{
525 int block, stop;
526
527 block = part->current_block == -1 ?
528 jiffies % part->total_blocks : part->current_block;
529 stop = block;
530
531 do {
532 if (part->blocks[block].free_sectors &&
533 block != part->reserved_block)
534 return block;
535
536 if (++block >= part->total_blocks)
537 block = 0;
538
539 } while (block != stop);
540
541 return -1;
542}
543
544static int find_writeable_block(struct partition *part, u_long *old_sector)
545{
546 int rc, block;
547 size_t retlen;
548
549 block = find_free_block(part);
550
551 if (block == -1) {
552 if (!part->is_reclaiming) {
553 rc = reclaim_block(part, old_sector);
554 if (rc)
555 goto err;
556
557 block = find_free_block(part);
558 }
559
560 if (block == -1) {
561 rc = -ENOSPC;
562 goto err;
563 }
564 }
565
566 rc = part->mbd.mtd->read(part->mbd.mtd, part->blocks[block].offset,
567 part->header_size, &retlen, (u_char*)part->header_cache);
568
569 if (!rc && retlen != part->header_size)
570 rc = -EIO;
571
572 if (rc) {
573 printk(KERN_NOTICE PREFIX "'%s': unable to read header at "
574 "0x%lx\n", part->mbd.mtd->name,
575 part->blocks[block].offset);
576 goto err;
577 }
578
579 part->current_block = block;
580
581err:
582 return rc;
583}
584
585static int mark_sector_deleted(struct partition *part, u_long old_addr)
586{
587 int block, offset, rc;
588 u_long addr;
589 size_t retlen;
590 u16 del = const_cpu_to_le16(SECTOR_DELETED);
591
592 block = old_addr / part->block_size;
593 offset = (old_addr % part->block_size) / SECTOR_SIZE -
594 part->header_sectors_per_block;
595
596 addr = part->blocks[block].offset +
597 (HEADER_MAP_OFFSET + offset) * sizeof(u16);
598 rc = part->mbd.mtd->write(part->mbd.mtd, addr,
599 sizeof(del), &retlen, (u_char*)&del);
600
601 if (!rc && retlen != sizeof(del))
602 rc = -EIO;
603
604 if (rc) {
605 printk(KERN_WARNING PREFIX "error writing '%s' at "
606 "0x%lx\n", part->mbd.mtd->name, addr);
607 if (rc)
608 goto err;
609 }
610 if (block == part->current_block)
611 part->header_cache[offset + HEADER_MAP_OFFSET] = del;
612
613 part->blocks[block].used_sectors--;
614
615 if (!part->blocks[block].used_sectors &&
616 !part->blocks[block].free_sectors)
617 rc = erase_block(part, block);
618
619err:
620 return rc;
621}
622
623static int find_free_sector(const struct partition *part, const struct block *block)
624{
625 int i, stop;
626
627 i = stop = part->data_sectors_per_block - block->free_sectors;
628
629 do {
630 if (le16_to_cpu(part->header_cache[HEADER_MAP_OFFSET + i])
631 == SECTOR_FREE)
632 return i;
633
634 if (++i == part->data_sectors_per_block)
635 i = 0;
636 }
637 while(i != stop);
638
639 return -1;
640}
641
642static int do_writesect(struct mtd_blktrans_dev *dev, u_long sector, char *buf, ulong *old_addr)
643{
644 struct partition *part = (struct partition*)dev;
645 struct block *block;
646 u_long addr;
647 int i;
648 int rc;
649 size_t retlen;
650 u16 entry;
651
652 if (part->current_block == -1 ||
653 !part->blocks[part->current_block].free_sectors) {
654
655 rc = find_writeable_block(part, old_addr);
656 if (rc)
657 goto err;
658 }
659
660 block = &part->blocks[part->current_block];
661
662 i = find_free_sector(part, block);
663
664 if (i < 0) {
665 rc = -ENOSPC;
666 goto err;
667 }
668
669 addr = (i + part->header_sectors_per_block) * SECTOR_SIZE +
670 block->offset;
671 rc = part->mbd.mtd->write(part->mbd.mtd,
672 addr, SECTOR_SIZE, &retlen, (u_char*)buf);
673
674 if (!rc && retlen != SECTOR_SIZE)
675 rc = -EIO;
676
677 if (rc) {
678 printk(KERN_WARNING PREFIX "error writing '%s' at 0x%lx\n",
679 part->mbd.mtd->name, addr);
680 if (rc)
681 goto err;
682 }
683
684 part->sector_map[sector] = addr;
685
686 entry = cpu_to_le16(sector == 0 ? SECTOR_ZERO : sector);
687
688 part->header_cache[i + HEADER_MAP_OFFSET] = entry;
689
690 addr = block->offset + (HEADER_MAP_OFFSET + i) * sizeof(u16);
691 rc = part->mbd.mtd->write(part->mbd.mtd, addr,
692 sizeof(entry), &retlen, (u_char*)&entry);
693
694 if (!rc && retlen != sizeof(entry))
695 rc = -EIO;
696
697 if (rc) {
698 printk(KERN_WARNING PREFIX "error writing '%s' at 0x%lx\n",
699 part->mbd.mtd->name, addr);
700 if (rc)
701 goto err;
702 }
703 block->used_sectors++;
704 block->free_sectors--;
705
706err:
707 return rc;
708}
709
710static int rfd_ftl_writesect(struct mtd_blktrans_dev *dev, u_long sector, char *buf)
711{
712 struct partition *part = (struct partition*)dev;
713 u_long old_addr;
714 int i;
715 int rc = 0;
716
717 pr_debug("rfd_ftl_writesect(sector=0x%lx)\n", sector);
718
719 if (part->reserved_block == -1) {
720 rc = -EACCES;
721 goto err;
722 }
723
724 if (sector >= part->sector_count) {
725 rc = -EIO;
726 goto err;
727 }
728
729 old_addr = part->sector_map[sector];
730
731 for (i=0; i<SECTOR_SIZE; i++) {
732 if (!buf[i])
733 continue;
734
735 rc = do_writesect(dev, sector, buf, &old_addr);
736 if (rc)
737 goto err;
738 break;
739 }
740
741 if (i == SECTOR_SIZE)
742 part->sector_map[sector] = -1;
743
744 if (old_addr != -1)
745 rc = mark_sector_deleted(part, old_addr);
746
747err:
748 return rc;
749}
750
751static int rfd_ftl_getgeo(struct mtd_blktrans_dev *dev, struct hd_geometry *geo)
752{
753 struct partition *part = (struct partition*)dev;
754
755 geo->heads = 1;
756 geo->sectors = SECTORS_PER_TRACK;
757 geo->cylinders = part->cylinders;
758
759 return 0;
760}
761
762static void rfd_ftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
763{
764 struct partition *part;
765
766 if (mtd->type != MTD_NORFLASH)
767 return;
768
769 part = kcalloc(1, sizeof(struct partition), GFP_KERNEL);
770 if (!part)
771 return;
772
773 part->mbd.mtd = mtd;
774
775 if (block_size)
776 part->block_size = block_size;
777 else {
778 if (!mtd->erasesize) {
779 printk(KERN_NOTICE PREFIX "please provide block_size");
780 return;
781 }
782 else
783 part->block_size = mtd->erasesize;
784 }
785
786 if (scan_header(part) == 0) {
787 part->mbd.size = part->sector_count;
788 part->mbd.blksize = SECTOR_SIZE;
789 part->mbd.tr = tr;
790 part->mbd.devnum = -1;
791 if (!(mtd->flags & MTD_WRITEABLE))
792 part->mbd.readonly = 1;
793 else if (part->errors) {
794 printk(KERN_NOTICE PREFIX "'%s': errors found, "
795 "setting read-only", mtd->name);
796 part->mbd.readonly = 1;
797 }
798
799 printk(KERN_INFO PREFIX "name: '%s' type: %d flags %x\n",
800 mtd->name, mtd->type, mtd->flags);
801
802 if (!add_mtd_blktrans_dev((void*)part))
803 return;
804 }
805
806 kfree(part);
807}
808
809static void rfd_ftl_remove_dev(struct mtd_blktrans_dev *dev)
810{
811 struct partition *part = (struct partition*)dev;
812 int i;
813
814 for (i=0; i<part->total_blocks; i++) {
815 pr_debug("rfd_ftl_remove_dev:'%s': erase unit #%02d: %d erases\n",
816 part->mbd.mtd->name, i, part->blocks[i].erases);
817 }
818
819 del_mtd_blktrans_dev(dev);
820 vfree(part->sector_map);
821 kfree(part->header_cache);
822 kfree(part->blocks);
823 kfree(part);
824}
825
826struct mtd_blktrans_ops rfd_ftl_tr = {
827 .name = "rfd",
828 .major = RFD_FTL_MAJOR,
829 .part_bits = PART_BITS,
830 .readsect = rfd_ftl_readsect,
831 .writesect = rfd_ftl_writesect,
832 .getgeo = rfd_ftl_getgeo,
833 .add_mtd = rfd_ftl_add_mtd,
834 .remove_dev = rfd_ftl_remove_dev,
835 .owner = THIS_MODULE,
836};
837
838static int __init init_rfd_ftl(void)
839{
840 return register_mtd_blktrans(&rfd_ftl_tr);
841}
842
843static void __exit cleanup_rfd_ftl(void)
844{
845 deregister_mtd_blktrans(&rfd_ftl_tr);
846}
847
848module_init(init_rfd_ftl);
849module_exit(cleanup_rfd_ftl);
850
851MODULE_LICENSE("GPL");
852MODULE_AUTHOR("Sean Young <sean@mess.org>");
853MODULE_DESCRIPTION("Support code for RFD Flash Translation Layer, "
854 "used by General Software's Embedded BIOS");
855