uboot/drivers/mtd/mtdconcat.c
<<
>>
Prefs
   1/*
   2 * MTD device concatenation layer
   3 *
   4 * (C) 2002 Robert Kaiser <rkaiser@sysgo.de>
   5 *
   6 * NAND support by Christian Gan <cgan@iders.ca>
   7 *
   8 * This code is GPL
   9 */
  10
  11#include <linux/mtd/mtd.h>
  12#include <linux/mtd/compat.h>
  13#include <linux/mtd/concat.h>
  14#include <ubi_uboot.h>
  15
  16/*
  17 * Our storage structure:
  18 * Subdev points to an array of pointers to struct mtd_info objects
  19 * which is allocated along with this structure
  20 *
  21 */
  22struct mtd_concat {
  23        struct mtd_info mtd;
  24        int num_subdev;
  25        struct mtd_info **subdev;
  26};
  27
  28/*
  29 * how to calculate the size required for the above structure,
  30 * including the pointer array subdev points to:
  31 */
  32#define SIZEOF_STRUCT_MTD_CONCAT(num_subdev)    \
  33        ((sizeof(struct mtd_concat) + (num_subdev) * sizeof(struct mtd_info *)))
  34
  35/*
  36 * Given a pointer to the MTD object in the mtd_concat structure,
  37 * we can retrieve the pointer to that structure with this macro.
  38 */
  39#define CONCAT(x)  ((struct mtd_concat *)(x))
  40
  41/*
  42 * MTD methods which look up the relevant subdevice, translate the
  43 * effective address and pass through to the subdevice.
  44 */
  45
  46static int
  47concat_read(struct mtd_info *mtd, loff_t from, size_t len,
  48            size_t * retlen, u_char * buf)
  49{
  50        struct mtd_concat *concat = CONCAT(mtd);
  51        int ret = 0, err;
  52        int i;
  53
  54        *retlen = 0;
  55
  56        for (i = 0; i < concat->num_subdev; i++) {
  57                struct mtd_info *subdev = concat->subdev[i];
  58                size_t size, retsize;
  59
  60                if (from >= subdev->size) {
  61                        /* Not destined for this subdev */
  62                        size = 0;
  63                        from -= subdev->size;
  64                        continue;
  65                }
  66                if (from + len > subdev->size)
  67                        /* First part goes into this subdev */
  68                        size = subdev->size - from;
  69                else
  70                        /* Entire transaction goes into this subdev */
  71                        size = len;
  72
  73                err = subdev->read(subdev, from, size, &retsize, buf);
  74
  75                /* Save information about bitflips! */
  76                if (unlikely(err)) {
  77                        if (err == -EBADMSG) {
  78                                mtd->ecc_stats.failed++;
  79                                ret = err;
  80                        } else if (err == -EUCLEAN) {
  81                                mtd->ecc_stats.corrected++;
  82                                /* Do not overwrite -EBADMSG !! */
  83                                if (!ret)
  84                                        ret = err;
  85                        } else
  86                                return err;
  87                }
  88
  89                *retlen += retsize;
  90                len -= size;
  91                if (len == 0)
  92                        return ret;
  93
  94                buf += size;
  95                from = 0;
  96        }
  97        return -EINVAL;
  98}
  99
 100static int
 101concat_write(struct mtd_info *mtd, loff_t to, size_t len,
 102             size_t * retlen, const u_char * buf)
 103{
 104        struct mtd_concat *concat = CONCAT(mtd);
 105        int err = -EINVAL;
 106        int i;
 107
 108        if (!(mtd->flags & MTD_WRITEABLE))
 109                return -EROFS;
 110
 111        *retlen = 0;
 112
 113        for (i = 0; i < concat->num_subdev; i++) {
 114                struct mtd_info *subdev = concat->subdev[i];
 115                size_t size, retsize;
 116
 117                if (to >= subdev->size) {
 118                        size = 0;
 119                        to -= subdev->size;
 120                        continue;
 121                }
 122                if (to + len > subdev->size)
 123                        size = subdev->size - to;
 124                else
 125                        size = len;
 126
 127                if (!(subdev->flags & MTD_WRITEABLE))
 128                        err = -EROFS;
 129                else
 130                        err = subdev->write(subdev, to, size, &retsize, buf);
 131
 132                if (err)
 133                        break;
 134
 135                *retlen += retsize;
 136                len -= size;
 137                if (len == 0)
 138                        break;
 139
 140                err = -EINVAL;
 141                buf += size;
 142                to = 0;
 143        }
 144        return err;
 145}
 146
 147static int
 148concat_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops)
 149{
 150        struct mtd_concat *concat = CONCAT(mtd);
 151        struct mtd_oob_ops devops = *ops;
 152        int i, err, ret = 0;
 153
 154        ops->retlen = ops->oobretlen = 0;
 155
 156        for (i = 0; i < concat->num_subdev; i++) {
 157                struct mtd_info *subdev = concat->subdev[i];
 158
 159                if (from >= subdev->size) {
 160                        from -= subdev->size;
 161                        continue;
 162                }
 163
 164                /* partial read ? */
 165                if (from + devops.len > subdev->size)
 166                        devops.len = subdev->size - from;
 167
 168                err = subdev->read_oob(subdev, from, &devops);
 169                ops->retlen += devops.retlen;
 170                ops->oobretlen += devops.oobretlen;
 171
 172                /* Save information about bitflips! */
 173                if (unlikely(err)) {
 174                        if (err == -EBADMSG) {
 175                                mtd->ecc_stats.failed++;
 176                                ret = err;
 177                        } else if (err == -EUCLEAN) {
 178                                mtd->ecc_stats.corrected++;
 179                                /* Do not overwrite -EBADMSG !! */
 180                                if (!ret)
 181                                        ret = err;
 182                        } else
 183                                return err;
 184                }
 185
 186                if (devops.datbuf) {
 187                        devops.len = ops->len - ops->retlen;
 188                        if (!devops.len)
 189                                return ret;
 190                        devops.datbuf += devops.retlen;
 191                }
 192                if (devops.oobbuf) {
 193                        devops.ooblen = ops->ooblen - ops->oobretlen;
 194                        if (!devops.ooblen)
 195                                return ret;
 196                        devops.oobbuf += ops->oobretlen;
 197                }
 198
 199                from = 0;
 200        }
 201        return -EINVAL;
 202}
 203
 204static int
 205concat_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops)
 206{
 207        struct mtd_concat *concat = CONCAT(mtd);
 208        struct mtd_oob_ops devops = *ops;
 209        int i, err;
 210
 211        if (!(mtd->flags & MTD_WRITEABLE))
 212                return -EROFS;
 213
 214        ops->retlen = 0;
 215
 216        for (i = 0; i < concat->num_subdev; i++) {
 217                struct mtd_info *subdev = concat->subdev[i];
 218
 219                if (to >= subdev->size) {
 220                        to -= subdev->size;
 221                        continue;
 222                }
 223
 224                /* partial write ? */
 225                if (to + devops.len > subdev->size)
 226                        devops.len = subdev->size - to;
 227
 228                err = subdev->write_oob(subdev, to, &devops);
 229                ops->retlen += devops.retlen;
 230                if (err)
 231                        return err;
 232
 233                if (devops.datbuf) {
 234                        devops.len = ops->len - ops->retlen;
 235                        if (!devops.len)
 236                                return 0;
 237                        devops.datbuf += devops.retlen;
 238                }
 239                if (devops.oobbuf) {
 240                        devops.ooblen = ops->ooblen - ops->oobretlen;
 241                        if (!devops.ooblen)
 242                                return 0;
 243                        devops.oobbuf += devops.oobretlen;
 244                }
 245                to = 0;
 246        }
 247        return -EINVAL;
 248}
 249
 250static void concat_erase_callback(struct erase_info *instr)
 251{
 252        /* Nothing to do here in U-Boot */
 253}
 254
 255static int concat_dev_erase(struct mtd_info *mtd, struct erase_info *erase)
 256{
 257        int err;
 258        wait_queue_head_t waitq;
 259        DECLARE_WAITQUEUE(wait, current);
 260
 261        /*
 262         * This code was stol^H^H^H^Hinspired by mtdchar.c
 263         */
 264        init_waitqueue_head(&waitq);
 265
 266        erase->mtd = mtd;
 267        erase->callback = concat_erase_callback;
 268        erase->priv = (unsigned long) &waitq;
 269
 270        /*
 271         * FIXME: Allow INTERRUPTIBLE. Which means
 272         * not having the wait_queue head on the stack.
 273         */
 274        err = mtd->erase(mtd, erase);
 275        if (!err) {
 276                set_current_state(TASK_UNINTERRUPTIBLE);
 277                add_wait_queue(&waitq, &wait);
 278                if (erase->state != MTD_ERASE_DONE
 279                    && erase->state != MTD_ERASE_FAILED)
 280                        schedule();
 281                remove_wait_queue(&waitq, &wait);
 282                set_current_state(TASK_RUNNING);
 283
 284                err = (erase->state == MTD_ERASE_FAILED) ? -EIO : 0;
 285        }
 286        return err;
 287}
 288
 289static int concat_erase(struct mtd_info *mtd, struct erase_info *instr)
 290{
 291        struct mtd_concat *concat = CONCAT(mtd);
 292        struct mtd_info *subdev;
 293        int i, err;
 294        uint64_t length, offset = 0;
 295        struct erase_info *erase;
 296
 297        if (!(mtd->flags & MTD_WRITEABLE))
 298                return -EROFS;
 299
 300        if (instr->addr > concat->mtd.size)
 301                return -EINVAL;
 302
 303        if (instr->len + instr->addr > concat->mtd.size)
 304                return -EINVAL;
 305
 306        /*
 307         * Check for proper erase block alignment of the to-be-erased area.
 308         * It is easier to do this based on the super device's erase
 309         * region info rather than looking at each particular sub-device
 310         * in turn.
 311         */
 312        if (!concat->mtd.numeraseregions) {
 313                /* the easy case: device has uniform erase block size */
 314                if (instr->addr & (concat->mtd.erasesize - 1))
 315                        return -EINVAL;
 316                if (instr->len & (concat->mtd.erasesize - 1))
 317                        return -EINVAL;
 318        } else {
 319                /* device has variable erase size */
 320                struct mtd_erase_region_info *erase_regions =
 321                    concat->mtd.eraseregions;
 322
 323                /*
 324                 * Find the erase region where the to-be-erased area begins:
 325                 */
 326                for (i = 0; i < concat->mtd.numeraseregions &&
 327                     instr->addr >= erase_regions[i].offset; i++) ;
 328                --i;
 329
 330                /*
 331                 * Now erase_regions[i] is the region in which the
 332                 * to-be-erased area begins. Verify that the starting
 333                 * offset is aligned to this region's erase size:
 334                 */
 335                if (instr->addr & (erase_regions[i].erasesize - 1))
 336                        return -EINVAL;
 337
 338                /*
 339                 * now find the erase region where the to-be-erased area ends:
 340                 */
 341                for (; i < concat->mtd.numeraseregions &&
 342                     (instr->addr + instr->len) >= erase_regions[i].offset;
 343                     ++i) ;
 344                --i;
 345                /*
 346                 * check if the ending offset is aligned to this region's erase size
 347                 */
 348                if ((instr->addr + instr->len) & (erase_regions[i].erasesize -
 349                                                  1))
 350                        return -EINVAL;
 351        }
 352
 353        instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN;
 354
 355        /* make a local copy of instr to avoid modifying the caller's struct */
 356        erase = kmalloc(sizeof (struct erase_info), GFP_KERNEL);
 357
 358        if (!erase)
 359                return -ENOMEM;
 360
 361        *erase = *instr;
 362        length = instr->len;
 363
 364        /*
 365         * find the subdevice where the to-be-erased area begins, adjust
 366         * starting offset to be relative to the subdevice start
 367         */
 368        for (i = 0; i < concat->num_subdev; i++) {
 369                subdev = concat->subdev[i];
 370                if (subdev->size <= erase->addr) {
 371                        erase->addr -= subdev->size;
 372                        offset += subdev->size;
 373                } else {
 374                        break;
 375                }
 376        }
 377
 378        /* must never happen since size limit has been verified above */
 379        BUG_ON(i >= concat->num_subdev);
 380
 381        /* now do the erase: */
 382        err = 0;
 383        for (; length > 0; i++) {
 384                /* loop for all subdevices affected by this request */
 385                subdev = concat->subdev[i];     /* get current subdevice */
 386
 387                /* limit length to subdevice's size: */
 388                if (erase->addr + length > subdev->size)
 389                        erase->len = subdev->size - erase->addr;
 390                else
 391                        erase->len = length;
 392
 393                if (!(subdev->flags & MTD_WRITEABLE)) {
 394                        err = -EROFS;
 395                        break;
 396                }
 397                length -= erase->len;
 398                if ((err = concat_dev_erase(subdev, erase))) {
 399                        /* sanity check: should never happen since
 400                         * block alignment has been checked above */
 401                        BUG_ON(err == -EINVAL);
 402                        if (erase->fail_addr != MTD_FAIL_ADDR_UNKNOWN)
 403                                instr->fail_addr = erase->fail_addr + offset;
 404                        break;
 405                }
 406                /*
 407                 * erase->addr specifies the offset of the area to be
 408                 * erased *within the current subdevice*. It can be
 409                 * non-zero only the first time through this loop, i.e.
 410                 * for the first subdevice where blocks need to be erased.
 411                 * All the following erases must begin at the start of the
 412                 * current subdevice, i.e. at offset zero.
 413                 */
 414                erase->addr = 0;
 415                offset += subdev->size;
 416        }
 417        instr->state = erase->state;
 418        kfree(erase);
 419        if (err)
 420                return err;
 421
 422        if (instr->callback)
 423                instr->callback(instr);
 424        return 0;
 425}
 426
 427static int concat_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
 428{
 429        struct mtd_concat *concat = CONCAT(mtd);
 430        int i, err = -EINVAL;
 431
 432        if ((len + ofs) > mtd->size)
 433                return -EINVAL;
 434
 435        for (i = 0; i < concat->num_subdev; i++) {
 436                struct mtd_info *subdev = concat->subdev[i];
 437                uint64_t size;
 438
 439                if (ofs >= subdev->size) {
 440                        size = 0;
 441                        ofs -= subdev->size;
 442                        continue;
 443                }
 444                if (ofs + len > subdev->size)
 445                        size = subdev->size - ofs;
 446                else
 447                        size = len;
 448
 449                err = subdev->lock(subdev, ofs, size);
 450
 451                if (err)
 452                        break;
 453
 454                len -= size;
 455                if (len == 0)
 456                        break;
 457
 458                err = -EINVAL;
 459                ofs = 0;
 460        }
 461
 462        return err;
 463}
 464
 465static int concat_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
 466{
 467        struct mtd_concat *concat = CONCAT(mtd);
 468        int i, err = 0;
 469
 470        if ((len + ofs) > mtd->size)
 471                return -EINVAL;
 472
 473        for (i = 0; i < concat->num_subdev; i++) {
 474                struct mtd_info *subdev = concat->subdev[i];
 475                uint64_t size;
 476
 477                if (ofs >= subdev->size) {
 478                        size = 0;
 479                        ofs -= subdev->size;
 480                        continue;
 481                }
 482                if (ofs + len > subdev->size)
 483                        size = subdev->size - ofs;
 484                else
 485                        size = len;
 486
 487                err = subdev->unlock(subdev, ofs, size);
 488
 489                if (err)
 490                        break;
 491
 492                len -= size;
 493                if (len == 0)
 494                        break;
 495
 496                err = -EINVAL;
 497                ofs = 0;
 498        }
 499
 500        return err;
 501}
 502
 503static void concat_sync(struct mtd_info *mtd)
 504{
 505        struct mtd_concat *concat = CONCAT(mtd);
 506        int i;
 507
 508        for (i = 0; i < concat->num_subdev; i++) {
 509                struct mtd_info *subdev = concat->subdev[i];
 510                subdev->sync(subdev);
 511        }
 512}
 513
 514static int concat_block_isbad(struct mtd_info *mtd, loff_t ofs)
 515{
 516        struct mtd_concat *concat = CONCAT(mtd);
 517        int i, res = 0;
 518
 519        if (!concat->subdev[0]->block_isbad)
 520                return res;
 521
 522        if (ofs > mtd->size)
 523                return -EINVAL;
 524
 525        for (i = 0; i < concat->num_subdev; i++) {
 526                struct mtd_info *subdev = concat->subdev[i];
 527
 528                if (ofs >= subdev->size) {
 529                        ofs -= subdev->size;
 530                        continue;
 531                }
 532
 533                res = subdev->block_isbad(subdev, ofs);
 534                break;
 535        }
 536
 537        return res;
 538}
 539
 540static int concat_block_markbad(struct mtd_info *mtd, loff_t ofs)
 541{
 542        struct mtd_concat *concat = CONCAT(mtd);
 543        int i, err = -EINVAL;
 544
 545        if (!concat->subdev[0]->block_markbad)
 546                return 0;
 547
 548        if (ofs > mtd->size)
 549                return -EINVAL;
 550
 551        for (i = 0; i < concat->num_subdev; i++) {
 552                struct mtd_info *subdev = concat->subdev[i];
 553
 554                if (ofs >= subdev->size) {
 555                        ofs -= subdev->size;
 556                        continue;
 557                }
 558
 559                err = subdev->block_markbad(subdev, ofs);
 560                if (!err)
 561                        mtd->ecc_stats.badblocks++;
 562                break;
 563        }
 564
 565        return err;
 566}
 567
 568/*
 569 * This function constructs a virtual MTD device by concatenating
 570 * num_devs MTD devices. A pointer to the new device object is
 571 * stored to *new_dev upon success. This function does _not_
 572 * register any devices: this is the caller's responsibility.
 573 */
 574struct mtd_info *mtd_concat_create(struct mtd_info *subdev[],   /* subdevices to concatenate */
 575                                   int num_devs,        /* number of subdevices      */
 576                                   const char *name)
 577{                               /* name for the new device   */
 578        int i;
 579        size_t size;
 580        struct mtd_concat *concat;
 581        uint32_t max_erasesize, curr_erasesize;
 582        int num_erase_region;
 583
 584        debug("Concatenating MTD devices:\n");
 585        for (i = 0; i < num_devs; i++)
 586                debug("(%d): \"%s\"\n", i, subdev[i]->name);
 587        debug("into device \"%s\"\n", name);
 588
 589        /* allocate the device structure */
 590        size = SIZEOF_STRUCT_MTD_CONCAT(num_devs);
 591        concat = kzalloc(size, GFP_KERNEL);
 592        if (!concat) {
 593                printk
 594                    ("memory allocation error while creating concatenated device \"%s\"\n",
 595                     name);
 596                return NULL;
 597        }
 598        concat->subdev = (struct mtd_info **) (concat + 1);
 599
 600        /*
 601         * Set up the new "super" device's MTD object structure, check for
 602         * incompatibilites between the subdevices.
 603         */
 604        concat->mtd.type = subdev[0]->type;
 605        concat->mtd.flags = subdev[0]->flags;
 606        concat->mtd.size = subdev[0]->size;
 607        concat->mtd.erasesize = subdev[0]->erasesize;
 608        concat->mtd.writesize = subdev[0]->writesize;
 609        concat->mtd.subpage_sft = subdev[0]->subpage_sft;
 610        concat->mtd.oobsize = subdev[0]->oobsize;
 611        concat->mtd.oobavail = subdev[0]->oobavail;
 612        if (subdev[0]->read_oob)
 613                concat->mtd.read_oob = concat_read_oob;
 614        if (subdev[0]->write_oob)
 615                concat->mtd.write_oob = concat_write_oob;
 616        if (subdev[0]->block_isbad)
 617                concat->mtd.block_isbad = concat_block_isbad;
 618        if (subdev[0]->block_markbad)
 619                concat->mtd.block_markbad = concat_block_markbad;
 620
 621        concat->mtd.ecc_stats.badblocks = subdev[0]->ecc_stats.badblocks;
 622
 623        concat->subdev[0] = subdev[0];
 624
 625        for (i = 1; i < num_devs; i++) {
 626                if (concat->mtd.type != subdev[i]->type) {
 627                        kfree(concat);
 628                        printk("Incompatible device type on \"%s\"\n",
 629                               subdev[i]->name);
 630                        return NULL;
 631                }
 632                if (concat->mtd.flags != subdev[i]->flags) {
 633                        /*
 634                         * Expect all flags except MTD_WRITEABLE to be
 635                         * equal on all subdevices.
 636                         */
 637                        if ((concat->mtd.flags ^ subdev[i]->
 638                             flags) & ~MTD_WRITEABLE) {
 639                                kfree(concat);
 640                                printk("Incompatible device flags on \"%s\"\n",
 641                                       subdev[i]->name);
 642                                return NULL;
 643                        } else
 644                                /* if writeable attribute differs,
 645                                   make super device writeable */
 646                                concat->mtd.flags |=
 647                                    subdev[i]->flags & MTD_WRITEABLE;
 648                }
 649
 650                concat->mtd.size += subdev[i]->size;
 651                concat->mtd.ecc_stats.badblocks +=
 652                        subdev[i]->ecc_stats.badblocks;
 653                if (concat->mtd.writesize   !=  subdev[i]->writesize ||
 654                    concat->mtd.subpage_sft != subdev[i]->subpage_sft ||
 655                    concat->mtd.oobsize    !=  subdev[i]->oobsize ||
 656                    !concat->mtd.read_oob  != !subdev[i]->read_oob ||
 657                    !concat->mtd.write_oob != !subdev[i]->write_oob) {
 658                        kfree(concat);
 659                        printk("Incompatible OOB or ECC data on \"%s\"\n",
 660                               subdev[i]->name);
 661                        return NULL;
 662                }
 663                concat->subdev[i] = subdev[i];
 664
 665        }
 666
 667        concat->mtd.ecclayout = subdev[0]->ecclayout;
 668
 669        concat->num_subdev = num_devs;
 670        concat->mtd.name = name;
 671
 672        concat->mtd.erase = concat_erase;
 673        concat->mtd.read = concat_read;
 674        concat->mtd.write = concat_write;
 675        concat->mtd.sync = concat_sync;
 676        concat->mtd.lock = concat_lock;
 677        concat->mtd.unlock = concat_unlock;
 678
 679        /*
 680         * Combine the erase block size info of the subdevices:
 681         *
 682         * first, walk the map of the new device and see how
 683         * many changes in erase size we have
 684         */
 685        max_erasesize = curr_erasesize = subdev[0]->erasesize;
 686        num_erase_region = 1;
 687        for (i = 0; i < num_devs; i++) {
 688                if (subdev[i]->numeraseregions == 0) {
 689                        /* current subdevice has uniform erase size */
 690                        if (subdev[i]->erasesize != curr_erasesize) {
 691                                /* if it differs from the last subdevice's erase size, count it */
 692                                ++num_erase_region;
 693                                curr_erasesize = subdev[i]->erasesize;
 694                                if (curr_erasesize > max_erasesize)
 695                                        max_erasesize = curr_erasesize;
 696                        }
 697                } else {
 698                        /* current subdevice has variable erase size */
 699                        int j;
 700                        for (j = 0; j < subdev[i]->numeraseregions; j++) {
 701
 702                                /* walk the list of erase regions, count any changes */
 703                                if (subdev[i]->eraseregions[j].erasesize !=
 704                                    curr_erasesize) {
 705                                        ++num_erase_region;
 706                                        curr_erasesize =
 707                                            subdev[i]->eraseregions[j].
 708                                            erasesize;
 709                                        if (curr_erasesize > max_erasesize)
 710                                                max_erasesize = curr_erasesize;
 711                                }
 712                        }
 713                }
 714        }
 715
 716        if (num_erase_region == 1) {
 717                /*
 718                 * All subdevices have the same uniform erase size.
 719                 * This is easy:
 720                 */
 721                concat->mtd.erasesize = curr_erasesize;
 722                concat->mtd.numeraseregions = 0;
 723        } else {
 724                uint64_t tmp64;
 725
 726                /*
 727                 * erase block size varies across the subdevices: allocate
 728                 * space to store the data describing the variable erase regions
 729                 */
 730                struct mtd_erase_region_info *erase_region_p;
 731                uint64_t begin, position;
 732
 733                concat->mtd.erasesize = max_erasesize;
 734                concat->mtd.numeraseregions = num_erase_region;
 735                concat->mtd.eraseregions = erase_region_p =
 736                    kmalloc(num_erase_region *
 737                            sizeof (struct mtd_erase_region_info), GFP_KERNEL);
 738                if (!erase_region_p) {
 739                        kfree(concat);
 740                        printk
 741                            ("memory allocation error while creating erase region list"
 742                             " for device \"%s\"\n", name);
 743                        return NULL;
 744                }
 745
 746                /*
 747                 * walk the map of the new device once more and fill in
 748                 * in erase region info:
 749                 */
 750                curr_erasesize = subdev[0]->erasesize;
 751                begin = position = 0;
 752                for (i = 0; i < num_devs; i++) {
 753                        if (subdev[i]->numeraseregions == 0) {
 754                                /* current subdevice has uniform erase size */
 755                                if (subdev[i]->erasesize != curr_erasesize) {
 756                                        /*
 757                                         *  fill in an mtd_erase_region_info structure for the area
 758                                         *  we have walked so far:
 759                                         */
 760                                        erase_region_p->offset = begin;
 761                                        erase_region_p->erasesize =
 762                                            curr_erasesize;
 763                                        tmp64 = position - begin;
 764                                        do_div(tmp64, curr_erasesize);
 765                                        erase_region_p->numblocks = tmp64;
 766                                        begin = position;
 767
 768                                        curr_erasesize = subdev[i]->erasesize;
 769                                        ++erase_region_p;
 770                                }
 771                                position += subdev[i]->size;
 772                        } else {
 773                                /* current subdevice has variable erase size */
 774                                int j;
 775                                for (j = 0; j < subdev[i]->numeraseregions; j++) {
 776                                        /* walk the list of erase regions, count any changes */
 777                                        if (subdev[i]->eraseregions[j].
 778                                            erasesize != curr_erasesize) {
 779                                                erase_region_p->offset = begin;
 780                                                erase_region_p->erasesize =
 781                                                    curr_erasesize;
 782                                                tmp64 = position - begin;
 783                                                do_div(tmp64, curr_erasesize);
 784                                                erase_region_p->numblocks = tmp64;
 785                                                begin = position;
 786
 787                                                curr_erasesize =
 788                                                    subdev[i]->eraseregions[j].
 789                                                    erasesize;
 790                                                ++erase_region_p;
 791                                        }
 792                                        position +=
 793                                            subdev[i]->eraseregions[j].
 794                                            numblocks * (uint64_t)curr_erasesize;
 795                                }
 796                        }
 797                }
 798                /* Now write the final entry */
 799                erase_region_p->offset = begin;
 800                erase_region_p->erasesize = curr_erasesize;
 801                tmp64 = position - begin;
 802                do_div(tmp64, curr_erasesize);
 803                erase_region_p->numblocks = tmp64;
 804        }
 805
 806        return &concat->mtd;
 807}
 808