ia64/linux-2.6.18-xen.hg

annotate drivers/mtd/rfd_ftl.c @ 0:831230e53067

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