uboot/drivers/mtd/mtdpart.c
<<
>>
Prefs
   1/*
   2 * Simple MTD partitioning layer
   3 *
   4 * (C) 2000 Nicolas Pitre <nico@cam.org>
   5 *
   6 * This code is GPL
   7 *
   8 *      02-21-2002      Thomas Gleixner <gleixner@autronix.de>
   9 *                      added support for read_oob, write_oob
  10 */
  11
  12#include <common.h>
  13#include <malloc.h>
  14#include <asm/errno.h>
  15
  16#include <linux/types.h>
  17#include <linux/list.h>
  18#include <linux/mtd/mtd.h>
  19#include <linux/mtd/partitions.h>
  20#include <linux/mtd/compat.h>
  21
  22/* Our partition linked list */
  23struct list_head mtd_partitions;
  24
  25/* Our partition node structure */
  26struct mtd_part {
  27        struct mtd_info mtd;
  28        struct mtd_info *master;
  29        uint64_t offset;
  30        int index;
  31        struct list_head list;
  32        int registered;
  33};
  34
  35/*
  36 * Given a pointer to the MTD object in the mtd_part structure, we can retrieve
  37 * the pointer to that structure with this macro.
  38 */
  39#define PART(x)  ((struct mtd_part *)(x))
  40
  41
  42/*
  43 * MTD methods which simply translate the effective address and pass through
  44 * to the _real_ device.
  45 */
  46
  47static int part_read(struct mtd_info *mtd, loff_t from, size_t len,
  48                size_t *retlen, u_char *buf)
  49{
  50        struct mtd_part *part = PART(mtd);
  51        struct mtd_ecc_stats stats;
  52        int res;
  53
  54        stats = part->master->ecc_stats;
  55
  56        if (from >= mtd->size)
  57                len = 0;
  58        else if (from + len > mtd->size)
  59                len = mtd->size - from;
  60        res = part->master->read(part->master, from + part->offset,
  61                                   len, retlen, buf);
  62        if (unlikely(res)) {
  63                if (res == -EUCLEAN)
  64                        mtd->ecc_stats.corrected += part->master->ecc_stats.corrected - stats.corrected;
  65                if (res == -EBADMSG)
  66                        mtd->ecc_stats.failed += part->master->ecc_stats.failed - stats.failed;
  67        }
  68        return res;
  69}
  70
  71static int part_read_oob(struct mtd_info *mtd, loff_t from,
  72                struct mtd_oob_ops *ops)
  73{
  74        struct mtd_part *part = PART(mtd);
  75        int res;
  76
  77        if (from >= mtd->size)
  78                return -EINVAL;
  79        if (ops->datbuf && from + ops->len > mtd->size)
  80                return -EINVAL;
  81        res = part->master->read_oob(part->master, from + part->offset, ops);
  82
  83        if (unlikely(res)) {
  84                if (res == -EUCLEAN)
  85                        mtd->ecc_stats.corrected++;
  86                if (res == -EBADMSG)
  87                        mtd->ecc_stats.failed++;
  88        }
  89        return res;
  90}
  91
  92static int part_read_user_prot_reg(struct mtd_info *mtd, loff_t from,
  93                size_t len, size_t *retlen, u_char *buf)
  94{
  95        struct mtd_part *part = PART(mtd);
  96        return part->master->read_user_prot_reg(part->master, from,
  97                                        len, retlen, buf);
  98}
  99
 100static int part_get_user_prot_info(struct mtd_info *mtd,
 101                struct otp_info *buf, size_t len)
 102{
 103        struct mtd_part *part = PART(mtd);
 104        return part->master->get_user_prot_info(part->master, buf, len);
 105}
 106
 107static int part_read_fact_prot_reg(struct mtd_info *mtd, loff_t from,
 108                size_t len, size_t *retlen, u_char *buf)
 109{
 110        struct mtd_part *part = PART(mtd);
 111        return part->master->read_fact_prot_reg(part->master, from,
 112                                        len, retlen, buf);
 113}
 114
 115static int part_get_fact_prot_info(struct mtd_info *mtd, struct otp_info *buf,
 116                size_t len)
 117{
 118        struct mtd_part *part = PART(mtd);
 119        return part->master->get_fact_prot_info(part->master, buf, len);
 120}
 121
 122static int part_write(struct mtd_info *mtd, loff_t to, size_t len,
 123                size_t *retlen, const u_char *buf)
 124{
 125        struct mtd_part *part = PART(mtd);
 126        if (!(mtd->flags & MTD_WRITEABLE))
 127                return -EROFS;
 128        if (to >= mtd->size)
 129                len = 0;
 130        else if (to + len > mtd->size)
 131                len = mtd->size - to;
 132        return part->master->write(part->master, to + part->offset,
 133                                    len, retlen, buf);
 134}
 135
 136static int part_panic_write(struct mtd_info *mtd, loff_t to, size_t len,
 137                size_t *retlen, const u_char *buf)
 138{
 139        struct mtd_part *part = PART(mtd);
 140        if (!(mtd->flags & MTD_WRITEABLE))
 141                return -EROFS;
 142        if (to >= mtd->size)
 143                len = 0;
 144        else if (to + len > mtd->size)
 145                len = mtd->size - to;
 146        return part->master->panic_write(part->master, to + part->offset,
 147                                    len, retlen, buf);
 148}
 149
 150static int part_write_oob(struct mtd_info *mtd, loff_t to,
 151                struct mtd_oob_ops *ops)
 152{
 153        struct mtd_part *part = PART(mtd);
 154
 155        if (!(mtd->flags & MTD_WRITEABLE))
 156                return -EROFS;
 157
 158        if (to >= mtd->size)
 159                return -EINVAL;
 160        if (ops->datbuf && to + ops->len > mtd->size)
 161                return -EINVAL;
 162        return part->master->write_oob(part->master, to + part->offset, ops);
 163}
 164
 165static int part_write_user_prot_reg(struct mtd_info *mtd, loff_t from,
 166                size_t len, size_t *retlen, u_char *buf)
 167{
 168        struct mtd_part *part = PART(mtd);
 169        return part->master->write_user_prot_reg(part->master, from,
 170                                        len, retlen, buf);
 171}
 172
 173static int part_lock_user_prot_reg(struct mtd_info *mtd, loff_t from,
 174                size_t len)
 175{
 176        struct mtd_part *part = PART(mtd);
 177        return part->master->lock_user_prot_reg(part->master, from, len);
 178}
 179
 180static int part_erase(struct mtd_info *mtd, struct erase_info *instr)
 181{
 182        struct mtd_part *part = PART(mtd);
 183        int ret;
 184        if (!(mtd->flags & MTD_WRITEABLE))
 185                return -EROFS;
 186        if (instr->addr >= mtd->size)
 187                return -EINVAL;
 188        instr->addr += part->offset;
 189        ret = part->master->erase(part->master, instr);
 190        if (ret) {
 191                if (instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN)
 192                        instr->fail_addr -= part->offset;
 193                instr->addr -= part->offset;
 194        }
 195        return ret;
 196}
 197
 198void mtd_erase_callback(struct erase_info *instr)
 199{
 200        if (instr->mtd->erase == part_erase) {
 201                struct mtd_part *part = PART(instr->mtd);
 202
 203                if (instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN)
 204                        instr->fail_addr -= part->offset;
 205                instr->addr -= part->offset;
 206        }
 207        if (instr->callback)
 208                instr->callback(instr);
 209}
 210
 211static int part_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
 212{
 213        struct mtd_part *part = PART(mtd);
 214        if ((len + ofs) > mtd->size)
 215                return -EINVAL;
 216        return part->master->lock(part->master, ofs + part->offset, len);
 217}
 218
 219static int part_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
 220{
 221        struct mtd_part *part = PART(mtd);
 222        if ((len + ofs) > mtd->size)
 223                return -EINVAL;
 224        return part->master->unlock(part->master, ofs + part->offset, len);
 225}
 226
 227static void part_sync(struct mtd_info *mtd)
 228{
 229        struct mtd_part *part = PART(mtd);
 230        part->master->sync(part->master);
 231}
 232
 233static int part_suspend(struct mtd_info *mtd)
 234{
 235        struct mtd_part *part = PART(mtd);
 236        return part->master->suspend(part->master);
 237}
 238
 239static void part_resume(struct mtd_info *mtd)
 240{
 241        struct mtd_part *part = PART(mtd);
 242        part->master->resume(part->master);
 243}
 244
 245static int part_block_isbad(struct mtd_info *mtd, loff_t ofs)
 246{
 247        struct mtd_part *part = PART(mtd);
 248        if (ofs >= mtd->size)
 249                return -EINVAL;
 250        ofs += part->offset;
 251        return part->master->block_isbad(part->master, ofs);
 252}
 253
 254static int part_block_markbad(struct mtd_info *mtd, loff_t ofs)
 255{
 256        struct mtd_part *part = PART(mtd);
 257        int res;
 258
 259        if (!(mtd->flags & MTD_WRITEABLE))
 260                return -EROFS;
 261        if (ofs >= mtd->size)
 262                return -EINVAL;
 263        ofs += part->offset;
 264        res = part->master->block_markbad(part->master, ofs);
 265        if (!res)
 266                mtd->ecc_stats.badblocks++;
 267        return res;
 268}
 269
 270/*
 271 * This function unregisters and destroy all slave MTD objects which are
 272 * attached to the given master MTD object.
 273 */
 274
 275int del_mtd_partitions(struct mtd_info *master)
 276{
 277        struct mtd_part *slave, *next;
 278
 279        list_for_each_entry_safe(slave, next, &mtd_partitions, list)
 280                if (slave->master == master) {
 281                        list_del(&slave->list);
 282                        if (slave->registered)
 283                                del_mtd_device(&slave->mtd);
 284                        kfree(slave);
 285                }
 286
 287        return 0;
 288}
 289
 290static struct mtd_part *add_one_partition(struct mtd_info *master,
 291                const struct mtd_partition *part, int partno,
 292                uint64_t cur_offset)
 293{
 294        struct mtd_part *slave;
 295
 296        /* allocate the partition structure */
 297        slave = kzalloc(sizeof(*slave), GFP_KERNEL);
 298        if (!slave) {
 299                printk(KERN_ERR"memory allocation error while creating partitions for \"%s\"\n",
 300                        master->name);
 301                del_mtd_partitions(master);
 302                return NULL;
 303        }
 304        list_add(&slave->list, &mtd_partitions);
 305
 306        /* set up the MTD object for this partition */
 307        slave->mtd.type = master->type;
 308        slave->mtd.flags = master->flags & ~part->mask_flags;
 309        slave->mtd.size = part->size;
 310        slave->mtd.writesize = master->writesize;
 311        slave->mtd.oobsize = master->oobsize;
 312        slave->mtd.oobavail = master->oobavail;
 313        slave->mtd.subpage_sft = master->subpage_sft;
 314
 315        slave->mtd.name = part->name;
 316        slave->mtd.owner = master->owner;
 317
 318        slave->mtd.read = part_read;
 319        slave->mtd.write = part_write;
 320
 321        if (master->panic_write)
 322                slave->mtd.panic_write = part_panic_write;
 323
 324        if (master->read_oob)
 325                slave->mtd.read_oob = part_read_oob;
 326        if (master->write_oob)
 327                slave->mtd.write_oob = part_write_oob;
 328        if (master->read_user_prot_reg)
 329                slave->mtd.read_user_prot_reg = part_read_user_prot_reg;
 330        if (master->read_fact_prot_reg)
 331                slave->mtd.read_fact_prot_reg = part_read_fact_prot_reg;
 332        if (master->write_user_prot_reg)
 333                slave->mtd.write_user_prot_reg = part_write_user_prot_reg;
 334        if (master->lock_user_prot_reg)
 335                slave->mtd.lock_user_prot_reg = part_lock_user_prot_reg;
 336        if (master->get_user_prot_info)
 337                slave->mtd.get_user_prot_info = part_get_user_prot_info;
 338        if (master->get_fact_prot_info)
 339                slave->mtd.get_fact_prot_info = part_get_fact_prot_info;
 340        if (master->sync)
 341                slave->mtd.sync = part_sync;
 342        if (!partno && master->suspend && master->resume) {
 343                        slave->mtd.suspend = part_suspend;
 344                        slave->mtd.resume = part_resume;
 345        }
 346        if (master->lock)
 347                slave->mtd.lock = part_lock;
 348        if (master->unlock)
 349                slave->mtd.unlock = part_unlock;
 350        if (master->block_isbad)
 351                slave->mtd.block_isbad = part_block_isbad;
 352        if (master->block_markbad)
 353                slave->mtd.block_markbad = part_block_markbad;
 354        slave->mtd.erase = part_erase;
 355        slave->master = master;
 356        slave->offset = part->offset;
 357        slave->index = partno;
 358
 359        if (slave->offset == MTDPART_OFS_APPEND)
 360                slave->offset = cur_offset;
 361        if (slave->offset == MTDPART_OFS_NXTBLK) {
 362                slave->offset = cur_offset;
 363                if (mtd_mod_by_eb(cur_offset, master) != 0) {
 364                        /* Round up to next erasesize */
 365                        slave->offset = (mtd_div_by_eb(cur_offset, master) + 1) * master->erasesize;
 366                        printk(KERN_NOTICE "Moving partition %d: "
 367                               "0x%012llx -> 0x%012llx\n", partno,
 368                               (unsigned long long)cur_offset, (unsigned long long)slave->offset);
 369                }
 370        }
 371        if (slave->mtd.size == MTDPART_SIZ_FULL)
 372                slave->mtd.size = master->size - slave->offset;
 373
 374        printk(KERN_NOTICE "0x%012llx-0x%012llx : \"%s\"\n", (unsigned long long)slave->offset,
 375                (unsigned long long)(slave->offset + slave->mtd.size), slave->mtd.name);
 376
 377        /* let's do some sanity checks */
 378        if (slave->offset >= master->size) {
 379                /* let's register it anyway to preserve ordering */
 380                slave->offset = 0;
 381                slave->mtd.size = 0;
 382                printk(KERN_ERR"mtd: partition \"%s\" is out of reach -- disabled\n",
 383                        part->name);
 384                goto out_register;
 385        }
 386        if (slave->offset + slave->mtd.size > master->size) {
 387                slave->mtd.size = master->size - slave->offset;
 388                printk(KERN_WARNING"mtd: partition \"%s\" extends beyond the end of device \"%s\" -- size truncated to %#llx\n",
 389                        part->name, master->name, (unsigned long long)slave->mtd.size);
 390        }
 391        if (master->numeraseregions > 1) {
 392                /* Deal with variable erase size stuff */
 393                int i, max = master->numeraseregions;
 394                u64 end = slave->offset + slave->mtd.size;
 395                struct mtd_erase_region_info *regions = master->eraseregions;
 396
 397                /* Find the first erase regions which is part of this
 398                 * partition. */
 399                for (i = 0; i < max && regions[i].offset <= slave->offset; i++)
 400                        ;
 401                /* The loop searched for the region _behind_ the first one */
 402                i--;
 403
 404                /* Pick biggest erasesize */
 405                for (; i < max && regions[i].offset < end; i++) {
 406                        if (slave->mtd.erasesize < regions[i].erasesize) {
 407                                slave->mtd.erasesize = regions[i].erasesize;
 408                        }
 409                }
 410                BUG_ON(slave->mtd.erasesize == 0);
 411        } else {
 412                /* Single erase size */
 413                slave->mtd.erasesize = master->erasesize;
 414        }
 415
 416        if ((slave->mtd.flags & MTD_WRITEABLE) &&
 417            mtd_mod_by_eb(slave->offset, &slave->mtd)) {
 418                /* Doesn't start on a boundary of major erase size */
 419                /* FIXME: Let it be writable if it is on a boundary of
 420                 * _minor_ erase size though */
 421                slave->mtd.flags &= ~MTD_WRITEABLE;
 422                printk(KERN_WARNING"mtd: partition \"%s\" doesn't start on an erase block boundary -- force read-only\n",
 423                        part->name);
 424        }
 425        if ((slave->mtd.flags & MTD_WRITEABLE) &&
 426            mtd_mod_by_eb(slave->mtd.size, &slave->mtd)) {
 427                slave->mtd.flags &= ~MTD_WRITEABLE;
 428                printk(KERN_WARNING"mtd: partition \"%s\" doesn't end on an erase block -- force read-only\n",
 429                        part->name);
 430        }
 431
 432        slave->mtd.ecclayout = master->ecclayout;
 433        if (master->block_isbad) {
 434                uint64_t offs = 0;
 435
 436                while (offs < slave->mtd.size) {
 437                        if (master->block_isbad(master,
 438                                                offs + slave->offset))
 439                                slave->mtd.ecc_stats.badblocks++;
 440                        offs += slave->mtd.erasesize;
 441                }
 442        }
 443
 444out_register:
 445        if (part->mtdp) {
 446                /* store the object pointer (caller may or may not register it*/
 447                *part->mtdp = &slave->mtd;
 448                slave->registered = 0;
 449        } else {
 450                /* register our partition */
 451                add_mtd_device(&slave->mtd);
 452                slave->registered = 1;
 453        }
 454        return slave;
 455}
 456
 457/*
 458 * This function, given a master MTD object and a partition table, creates
 459 * and registers slave MTD objects which are bound to the master according to
 460 * the partition definitions.
 461 *
 462 * We don't register the master, or expect the caller to have done so,
 463 * for reasons of data integrity.
 464 */
 465
 466int add_mtd_partitions(struct mtd_info *master,
 467                       const struct mtd_partition *parts,
 468                       int nbparts)
 469{
 470        struct mtd_part *slave;
 471        uint64_t cur_offset = 0;
 472        int i;
 473
 474        /*
 475         * Need to init the list here, since LIST_INIT() does not
 476         * work on platforms where relocation has problems (like MIPS
 477         * & PPC).
 478         */
 479        if (mtd_partitions.next == NULL)
 480                INIT_LIST_HEAD(&mtd_partitions);
 481
 482        printk(KERN_NOTICE "Creating %d MTD partitions on \"%s\":\n", nbparts, master->name);
 483
 484        for (i = 0; i < nbparts; i++) {
 485                slave = add_one_partition(master, parts + i, i, cur_offset);
 486                if (!slave)
 487                        return -ENOMEM;
 488                cur_offset = slave->offset + slave->mtd.size;
 489        }
 490
 491        return 0;
 492}
 493