| 1 | |
| 2 | |
| 3 | * Simple MTD partitioning layer
|
| 4 | *
|
| 5 | * Copyright © 2000 Nicolas Pitre <[email protected]>
|
| 6 | * Copyright © 2002 Thomas Gleixner <[email protected]>
|
| 7 | * Copyright © 2000-2010 David Woodhouse <[email protected]>
|
| 8 |
|
| 9 |
|
| 10 | #include <linux/module.h>
|
| 11 | #include <linux/types.h>
|
| 12 | #include <linux/kernel.h>
|
| 13 | #include <linux/slab.h>
|
| 14 | #include <linux/list.h>
|
| 15 | #include <linux/kmod.h>
|
| 16 | #include <linux/mtd/mtd.h>
|
| 17 | #include <linux/mtd/partitions.h>
|
| 18 | #include <linux/err.h>
|
| 19 | #include <linux/of.h>
|
| 20 | #include <linux/of_platform.h>
|
| 21 | #include <linux/mtd/concat.h>
|
| 22 |
|
| 23 | #include "mtdcore.h"
|
| 24 |
|
| 25 | |
| 26 | * MTD methods which simply translate the effective address and pass through
|
| 27 | * to the _real_ device.
|
| 28 |
|
| 29 |
|
| 30 | static inline void free_partition(struct mtd_info *mtd)
|
| 31 | {
|
| 32 | kfree(mtd->name);
|
| 33 | kfree(mtd);
|
| 34 | }
|
| 35 |
|
| 36 | void release_mtd_partition(struct mtd_info *mtd)
|
| 37 | {
|
| 38 | WARN_ON(!list_empty(&mtd->part.node));
|
| 39 | free_partition(mtd);
|
| 40 | }
|
| 41 |
|
| 42 | static struct mtd_info *allocate_partition(struct mtd_info *parent,
|
| 43 | const struct mtd_partition *part,
|
| 44 | int partno, uint64_t cur_offset)
|
| 45 | {
|
| 46 | struct mtd_info *master = mtd_get_master(parent);
|
| 47 | int wr_alignment = (parent->flags & MTD_NO_ERASE) ?
|
| 48 | master->writesize : master->erasesize;
|
| 49 | u64 parent_size = mtd_is_partition(parent) ?
|
| 50 | parent->part.size : parent->size;
|
| 51 | struct mtd_info *child;
|
| 52 | u32 remainder;
|
| 53 | char *name;
|
| 54 | u64 tmp;
|
| 55 |
|
| 56 |
|
| 57 | child = kzalloc_obj(*child);
|
| 58 | name = kstrdup(part->name, GFP_KERNEL);
|
| 59 | if (!name || !child) {
|
| 60 | printk(KERN_ERR"memory allocation error while creating partitions for \"%s\"\n",
|
| 61 | parent->name);
|
| 62 | kfree(name);
|
| 63 | kfree(child);
|
| 64 | return ERR_PTR(-ENOMEM);
|
| 65 | }
|
| 66 |
|
| 67 |
|
| 68 | child->type = parent->type;
|
| 69 | child->part.flags = parent->flags & ~part->mask_flags;
|
| 70 | child->part.flags |= part->add_flags;
|
| 71 | child->flags = child->part.flags;
|
| 72 | child->part.size = part->size;
|
| 73 | child->writesize = parent->writesize;
|
| 74 | child->writebufsize = parent->writebufsize;
|
| 75 | child->oobsize = parent->oobsize;
|
| 76 | child->oobavail = parent->oobavail;
|
| 77 | child->subpage_sft = parent->subpage_sft;
|
| 78 |
|
| 79 | child->name = name;
|
| 80 | child->owner = parent->owner;
|
| 81 |
|
| 82 | |
| 83 | * concern for showing the same data in multiple partitions.
|
| 84 | * However, it is very useful to have the master node present,
|
| 85 | * so the MTD_PARTITIONED_MASTER option allows that. The master
|
| 86 | * will have device nodes etc only if this is set, so make the
|
| 87 | * parent conditional on that option. Note, this is a way to
|
| 88 | * distinguish between the parent and its partitions in sysfs.
|
| 89 |
|
| 90 | child->dev.parent = IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER) || mtd_is_partition(parent) ?
|
| 91 | &parent->dev : parent->dev.parent;
|
| 92 | child->dev.of_node = part->of_node;
|
| 93 | child->parent = parent;
|
| 94 | child->part.offset = part->offset;
|
| 95 | INIT_LIST_HEAD(&child->partitions);
|
| 96 |
|
| 97 | if (child->part.offset == MTDPART_OFS_APPEND)
|
| 98 | child->part.offset = cur_offset;
|
| 99 | if (child->part.offset == MTDPART_OFS_NXTBLK) {
|
| 100 | tmp = cur_offset;
|
| 101 | child->part.offset = cur_offset;
|
| 102 | remainder = do_div(tmp, wr_alignment);
|
| 103 | if (remainder) {
|
| 104 | child->part.offset += wr_alignment - remainder;
|
| 105 | printk(KERN_NOTICE "Moving partition %d: "
|
| 106 | "0x%012llx -> 0x%012llx\n", partno,
|
| 107 | (unsigned long long)cur_offset,
|
| 108 | child->part.offset);
|
| 109 | }
|
| 110 | }
|
| 111 | if (child->part.offset == MTDPART_OFS_RETAIN) {
|
| 112 | child->part.offset = cur_offset;
|
| 113 | if (parent_size - child->part.offset >= child->part.size) {
|
| 114 | child->part.size = parent_size - child->part.offset -
|
| 115 | child->part.size;
|
| 116 | } else {
|
| 117 | printk(KERN_ERR "mtd partition \"%s\" doesn't have enough space: %#llx < %#llx, disabled\n",
|
| 118 | part->name, parent_size - child->part.offset,
|
| 119 | child->part.size);
|
| 120 |
|
| 121 | child->part.offset = 0;
|
| 122 | child->part.size = 0;
|
| 123 | child->erasesize = parent->erasesize;
|
| 124 | goto out_register;
|
| 125 | }
|
| 126 | }
|
| 127 | if (child->part.size == MTDPART_SIZ_FULL)
|
| 128 | child->part.size = parent_size - child->part.offset;
|
| 129 |
|
| 130 | printk(KERN_NOTICE "0x%012llx-0x%012llx : \"%s\"\n",
|
| 131 | child->part.offset, child->part.offset + child->part.size,
|
| 132 | child->name);
|
| 133 |
|
| 134 |
|
| 135 | if (child->part.offset >= parent_size) {
|
| 136 |
|
| 137 | child->part.offset = 0;
|
| 138 | child->part.size = 0;
|
| 139 |
|
| 140 |
|
| 141 | child->erasesize = parent->erasesize;
|
| 142 | printk(KERN_ERR"mtd: partition \"%s\" is out of reach -- disabled\n",
|
| 143 | part->name);
|
| 144 | goto out_register;
|
| 145 | }
|
| 146 | if (child->part.offset + child->part.size > parent->size) {
|
| 147 | child->part.size = parent_size - child->part.offset;
|
| 148 | printk(KERN_WARNING"mtd: partition \"%s\" extends beyond the end of device \"%s\" -- size truncated to %#llx\n",
|
| 149 | part->name, parent->name, child->part.size);
|
| 150 | }
|
| 151 |
|
| 152 | if (parent->numeraseregions > 1) {
|
| 153 |
|
| 154 | int i, max = parent->numeraseregions;
|
| 155 | u64 end = child->part.offset + child->part.size;
|
| 156 | struct mtd_erase_region_info *regions = parent->eraseregions;
|
| 157 |
|
| 158 | |
| 159 | * partition.
|
| 160 | for (i = 0; i < max && regions[i].offset <= child->part.offset;
|
| 161 | i++)
|
| 162 | ;
|
| 163 |
|
| 164 | if (i > 0)
|
| 165 | i--;
|
| 166 |
|
| 167 |
|
| 168 | for (; i < max && regions[i].offset < end; i++) {
|
| 169 | if (child->erasesize < regions[i].erasesize)
|
| 170 | child->erasesize = regions[i].erasesize;
|
| 171 | }
|
| 172 | BUG_ON(child->erasesize == 0);
|
| 173 | } else {
|
| 174 |
|
| 175 | child->erasesize = master->erasesize;
|
| 176 | }
|
| 177 |
|
| 178 | |
| 179 | * Child erasesize might differ from the parent one if the parent
|
| 180 | * exposes several regions with different erasesize. Adjust
|
| 181 | * wr_alignment accordingly.
|
| 182 |
|
| 183 | if (!(child->flags & MTD_NO_ERASE))
|
| 184 | wr_alignment = child->erasesize;
|
| 185 |
|
| 186 | tmp = mtd_get_master_ofs(child, 0);
|
| 187 | remainder = do_div(tmp, wr_alignment);
|
| 188 | if ((child->flags & MTD_WRITEABLE) && remainder) {
|
| 189 |
|
| 190 | |
| 191 | * _minor_ erase size though
|
| 192 | child->flags &= ~MTD_WRITEABLE;
|
| 193 | printk(KERN_WARNING"mtd: partition \"%s\" doesn't start on an erase/write block boundary -- force read-only\n",
|
| 194 | part->name);
|
| 195 | }
|
| 196 |
|
| 197 | tmp = mtd_get_master_ofs(child, 0) + child->part.size;
|
| 198 | remainder = do_div(tmp, wr_alignment);
|
| 199 | if ((child->flags & MTD_WRITEABLE) && remainder) {
|
| 200 | child->flags &= ~MTD_WRITEABLE;
|
| 201 | printk(KERN_WARNING"mtd: partition \"%s\" doesn't end on an erase/write block -- force read-only\n",
|
| 202 | part->name);
|
| 203 | }
|
| 204 |
|
| 205 | child->size = child->part.size;
|
| 206 | child->ecc_step_size = parent->ecc_step_size;
|
| 207 | child->ecc_strength = parent->ecc_strength;
|
| 208 | child->bitflip_threshold = parent->bitflip_threshold;
|
| 209 |
|
| 210 | if (master->_block_isbad) {
|
| 211 | uint64_t offs = 0;
|
| 212 |
|
| 213 | while (offs < child->part.size) {
|
| 214 | if (mtd_block_isreserved(child, offs))
|
| 215 | child->ecc_stats.bbtblocks++;
|
| 216 | else if (mtd_block_isbad(child, offs))
|
| 217 | child->ecc_stats.badblocks++;
|
| 218 | offs += child->erasesize;
|
| 219 | }
|
| 220 | }
|
| 221 |
|
| 222 | out_register:
|
| 223 | return child;
|
| 224 | }
|
| 225 |
|
| 226 | static ssize_t offset_show(struct device *dev,
|
| 227 | struct device_attribute *attr, char *buf)
|
| 228 | {
|
| 229 | struct mtd_info *mtd = dev_get_drvdata(dev);
|
| 230 |
|
| 231 | return sysfs_emit(buf, "%lld\n", mtd->part.offset);
|
| 232 | }
|
| 233 | static DEVICE_ATTR_RO(offset);
|
| 234 |
|
| 235 | static const struct attribute *mtd_partition_attrs[] = {
|
| 236 | &dev_attr_offset.attr,
|
| 237 | NULL
|
| 238 | };
|
| 239 |
|
| 240 | static int mtd_add_partition_attrs(struct mtd_info *new)
|
| 241 | {
|
| 242 | int ret = sysfs_create_files(&new->dev.kobj, mtd_partition_attrs);
|
| 243 | if (ret)
|
| 244 | printk(KERN_WARNING
|
| 245 | "mtd: failed to create partition attrs, err=%d\n", ret);
|
| 246 | return ret;
|
| 247 | }
|
| 248 |
|
| 249 | int mtd_add_partition(struct mtd_info *parent, const char *name,
|
| 250 | long long offset, long long length)
|
| 251 | {
|
| 252 | struct mtd_info *master = mtd_get_master(parent);
|
| 253 | u64 parent_size = mtd_is_partition(parent) ?
|
| 254 | parent->part.size : parent->size;
|
| 255 | struct mtd_partition part;
|
| 256 | struct mtd_info *child;
|
| 257 | int ret = 0;
|
| 258 |
|
| 259 |
|
| 260 | if (offset == MTDPART_OFS_APPEND ||
|
| 261 | offset == MTDPART_OFS_NXTBLK ||
|
| 262 | offset == MTDPART_OFS_RETAIN)
|
| 263 | return -EINVAL;
|
| 264 |
|
| 265 | if (length == MTDPART_SIZ_FULL)
|
| 266 | length = parent_size - offset;
|
| 267 |
|
| 268 | if (length <= 0)
|
| 269 | return -EINVAL;
|
| 270 |
|
| 271 | if (offset < 0 || offset >= (long long)parent_size)
|
| 272 | return -EINVAL;
|
| 273 |
|
| 274 | if ((u64)offset + (u64)length > parent_size)
|
| 275 | return -EINVAL;
|
| 276 | memset(&part, 0, sizeof(part));
|
| 277 | part.name = name;
|
| 278 | part.size = length;
|
| 279 | part.offset = offset;
|
| 280 |
|
| 281 | child = allocate_partition(parent, &part, -1, offset);
|
| 282 | if (IS_ERR(child))
|
| 283 | return PTR_ERR(child);
|
| 284 |
|
| 285 | mutex_lock(&master->master.partitions_lock);
|
| 286 | list_add_tail(&child->part.node, &parent->partitions);
|
| 287 | mutex_unlock(&master->master.partitions_lock);
|
| 288 |
|
| 289 | ret = add_mtd_device(child);
|
| 290 | if (ret)
|
| 291 | goto err_remove_part;
|
| 292 |
|
| 293 | mtd_add_partition_attrs(child);
|
| 294 |
|
| 295 | return 0;
|
| 296 |
|
| 297 | err_remove_part:
|
| 298 | mutex_lock(&master->master.partitions_lock);
|
| 299 | list_del(&child->part.node);
|
| 300 | mutex_unlock(&master->master.partitions_lock);
|
| 301 |
|
| 302 | free_partition(child);
|
| 303 |
|
| 304 | return ret;
|
| 305 | }
|
| 306 | EXPORT_SYMBOL_GPL(mtd_add_partition);
|
| 307 |
|
| 308 | |
| 309 | * __mtd_del_partition - delete MTD partition
|
| 310 | *
|
| 311 | * @mtd: MTD structure to be deleted
|
| 312 | *
|
| 313 | * This function must be called with the partitions mutex locked.
|
| 314 |
|
| 315 | static int __mtd_del_partition(struct mtd_info *mtd)
|
| 316 | {
|
| 317 | struct mtd_info *child, *next;
|
| 318 | int err;
|
| 319 |
|
| 320 | list_for_each_entry_safe(child, next, &mtd->partitions, part.node) {
|
| 321 | err = __mtd_del_partition(child);
|
| 322 | if (err)
|
| 323 | return err;
|
| 324 | }
|
| 325 |
|
| 326 | sysfs_remove_files(&mtd->dev.kobj, mtd_partition_attrs);
|
| 327 |
|
| 328 | list_del_init(&mtd->part.node);
|
| 329 | err = del_mtd_device(mtd);
|
| 330 | if (err)
|
| 331 | return err;
|
| 332 |
|
| 333 | return 0;
|
| 334 | }
|
| 335 |
|
| 336 | |
| 337 | * This function unregisters and destroy all slave MTD objects which are
|
| 338 | * attached to the given MTD object, recursively.
|
| 339 |
|
| 340 | static int __del_mtd_partitions(struct mtd_info *mtd)
|
| 341 | {
|
| 342 | struct mtd_info *child, *next;
|
| 343 | int ret, err = 0;
|
| 344 |
|
| 345 | list_for_each_entry_safe(child, next, &mtd->partitions, part.node) {
|
| 346 | if (mtd_has_partitions(child))
|
| 347 | __del_mtd_partitions(child);
|
| 348 |
|
| 349 | pr_info("Deleting %s MTD partition\n", child->name);
|
| 350 | list_del_init(&child->part.node);
|
| 351 | ret = del_mtd_device(child);
|
| 352 | if (ret < 0) {
|
| 353 | pr_err("Error when deleting partition \"%s\" (%d)\n",
|
| 354 | child->name, ret);
|
| 355 | err = ret;
|
| 356 | continue;
|
| 357 | }
|
| 358 | }
|
| 359 |
|
| 360 | return err;
|
| 361 | }
|
| 362 |
|
| 363 | int del_mtd_partitions(struct mtd_info *mtd)
|
| 364 | {
|
| 365 | struct mtd_info *master = mtd_get_master(mtd);
|
| 366 | int ret;
|
| 367 |
|
| 368 | pr_info("Deleting MTD partitions on \"%s\":\n", mtd->name);
|
| 369 |
|
| 370 | mutex_lock(&master->master.partitions_lock);
|
| 371 | ret = __del_mtd_partitions(mtd);
|
| 372 | mutex_unlock(&master->master.partitions_lock);
|
| 373 |
|
| 374 | return ret;
|
| 375 | }
|
| 376 |
|
| 377 | int mtd_del_partition(struct mtd_info *mtd, int partno)
|
| 378 | {
|
| 379 | struct mtd_info *child, *master = mtd_get_master(mtd);
|
| 380 | int ret = -EINVAL;
|
| 381 |
|
| 382 | mutex_lock(&master->master.partitions_lock);
|
| 383 | list_for_each_entry(child, &mtd->partitions, part.node) {
|
| 384 | if (child->index == partno) {
|
| 385 | ret = __mtd_del_partition(child);
|
| 386 | break;
|
| 387 | }
|
| 388 | }
|
| 389 | mutex_unlock(&master->master.partitions_lock);
|
| 390 |
|
| 391 | return ret;
|
| 392 | }
|
| 393 | EXPORT_SYMBOL_GPL(mtd_del_partition);
|
| 394 |
|
| 395 | |
| 396 | * This function, given a parent MTD object and a partition table, creates
|
| 397 | * and registers the child MTD objects which are bound to the parent according
|
| 398 | * to the partition definitions.
|
| 399 | *
|
| 400 | * For historical reasons, this function's caller only registers the parent
|
| 401 | * if the MTD_PARTITIONED_MASTER config option is set.
|
| 402 |
|
| 403 |
|
| 404 | int add_mtd_partitions(struct mtd_info *parent,
|
| 405 | const struct mtd_partition *parts,
|
| 406 | int nbparts)
|
| 407 | {
|
| 408 | struct mtd_info *child, *master = mtd_get_master(parent);
|
| 409 | uint64_t cur_offset = 0;
|
| 410 | int i, ret;
|
| 411 |
|
| 412 | printk(KERN_NOTICE "Creating %d MTD partitions on \"%s\":\n",
|
| 413 | nbparts, parent->name);
|
| 414 |
|
| 415 | for (i = 0; i < nbparts; i++) {
|
| 416 | child = allocate_partition(parent, parts + i, i, cur_offset);
|
| 417 | if (IS_ERR(child)) {
|
| 418 | ret = PTR_ERR(child);
|
| 419 | goto err_del_partitions;
|
| 420 | }
|
| 421 |
|
| 422 | if (IS_REACHABLE(CONFIG_MTD_VIRT_CONCAT)) {
|
| 423 | if (mtd_virt_concat_add(child))
|
| 424 | continue;
|
| 425 | }
|
| 426 |
|
| 427 | mutex_lock(&master->master.partitions_lock);
|
| 428 | list_add_tail(&child->part.node, &parent->partitions);
|
| 429 | mutex_unlock(&master->master.partitions_lock);
|
| 430 |
|
| 431 | ret = add_mtd_device(child);
|
| 432 | if (ret) {
|
| 433 | mutex_lock(&master->master.partitions_lock);
|
| 434 | list_del(&child->part.node);
|
| 435 | mutex_unlock(&master->master.partitions_lock);
|
| 436 |
|
| 437 | free_partition(child);
|
| 438 | goto err_del_partitions;
|
| 439 | }
|
| 440 |
|
| 441 | mtd_add_partition_attrs(child);
|
| 442 |
|
| 443 |
|
| 444 | ret = parse_mtd_partitions(child, parts[i].types, NULL);
|
| 445 | if (ret < 0 && ret == -ENOENT) {
|
| 446 | pr_debug("Skip parsing subpartitions: %d\n", ret);
|
| 447 | continue;
|
| 448 | } else if (ret < 0) {
|
| 449 | pr_err("Failed to parse subpartitions: %d\n", ret);
|
| 450 | goto err_del_partitions;
|
| 451 | }
|
| 452 |
|
| 453 | cur_offset = child->part.offset + child->part.size;
|
| 454 | }
|
| 455 |
|
| 456 | return 0;
|
| 457 |
|
| 458 | err_del_partitions:
|
| 459 | del_mtd_partitions(master);
|
| 460 |
|
| 461 | return ret;
|
| 462 | }
|
| 463 |
|
| 464 | static DEFINE_SPINLOCK(part_parser_lock);
|
| 465 | static LIST_HEAD(part_parsers);
|
| 466 |
|
| 467 | static struct mtd_part_parser *mtd_part_parser_get(const char *name)
|
| 468 | {
|
| 469 | struct mtd_part_parser *p, *ret = NULL;
|
| 470 |
|
| 471 | spin_lock(&part_parser_lock);
|
| 472 |
|
| 473 | list_for_each_entry(p, &part_parsers, list)
|
| 474 | if (!strcmp(p->name, name) && try_module_get(p->owner)) {
|
| 475 | ret = p;
|
| 476 | break;
|
| 477 | }
|
| 478 |
|
| 479 | spin_unlock(&part_parser_lock);
|
| 480 |
|
| 481 | return ret;
|
| 482 | }
|
| 483 |
|
| 484 | static inline void mtd_part_parser_put(const struct mtd_part_parser *p)
|
| 485 | {
|
| 486 | module_put(p->owner);
|
| 487 | }
|
| 488 |
|
| 489 | |
| 490 | * Many partition parsers just expected the core to kfree() all their data in
|
| 491 | * one chunk. Do that by default.
|
| 492 |
|
| 493 | static void mtd_part_parser_cleanup_default(const struct mtd_partition *pparts,
|
| 494 | int nr_parts)
|
| 495 | {
|
| 496 | kfree(pparts);
|
| 497 | }
|
| 498 |
|
| 499 | int __register_mtd_parser(struct mtd_part_parser *p, struct module *owner)
|
| 500 | {
|
| 501 | p->owner = owner;
|
| 502 |
|
| 503 | if (!p->cleanup)
|
| 504 | p->cleanup = &mtd_part_parser_cleanup_default;
|
| 505 |
|
| 506 | spin_lock(&part_parser_lock);
|
| 507 | list_add(&p->list, &part_parsers);
|
| 508 | spin_unlock(&part_parser_lock);
|
| 509 |
|
| 510 | return 0;
|
| 511 | }
|
| 512 | EXPORT_SYMBOL_GPL(__register_mtd_parser);
|
| 513 |
|
| 514 | void deregister_mtd_parser(struct mtd_part_parser *p)
|
| 515 | {
|
| 516 | spin_lock(&part_parser_lock);
|
| 517 | list_del(&p->list);
|
| 518 | spin_unlock(&part_parser_lock);
|
| 519 | }
|
| 520 | EXPORT_SYMBOL_GPL(deregister_mtd_parser);
|
| 521 |
|
| 522 | |
| 523 | * Do not forget to update 'parse_mtd_partitions()' kerneldoc comment if you
|
| 524 | * are changing this array!
|
| 525 |
|
| 526 | static const char * const default_mtd_part_types[] = {
|
| 527 | "cmdlinepart",
|
| 528 | "ofpart",
|
| 529 | NULL
|
| 530 | };
|
| 531 |
|
| 532 |
|
| 533 | static const char * const default_subpartition_types[] = {
|
| 534 | "ofpart",
|
| 535 | NULL
|
| 536 | };
|
| 537 |
|
| 538 | static int mtd_part_do_parse(struct mtd_part_parser *parser,
|
| 539 | struct mtd_info *master,
|
| 540 | struct mtd_partitions *pparts,
|
| 541 | struct mtd_part_parser_data *data)
|
| 542 | {
|
| 543 | int ret;
|
| 544 |
|
| 545 | ret = (*parser->parse_fn)(master, &pparts->parts, data);
|
| 546 | pr_debug("%s: parser %s: %i\n", master->name, parser->name, ret);
|
| 547 | if (ret <= 0)
|
| 548 | return ret;
|
| 549 |
|
| 550 | pr_notice("%d %s partitions found on MTD device %s\n", ret,
|
| 551 | parser->name, master->name);
|
| 552 |
|
| 553 | pparts->nr_parts = ret;
|
| 554 | pparts->parser = parser;
|
| 555 |
|
| 556 | return ret;
|
| 557 | }
|
| 558 |
|
| 559 | |
| 560 | * mtd_part_get_compatible_parser - find MTD parser by a compatible string
|
| 561 | *
|
| 562 | * @compat: compatible string describing partitions in a device tree
|
| 563 | *
|
| 564 | * MTD parsers can specify supported partitions by providing a table of
|
| 565 | * compatibility strings. This function finds a parser that advertises support
|
| 566 | * for a passed value of "compatible".
|
| 567 |
|
| 568 | static struct mtd_part_parser *mtd_part_get_compatible_parser(const char *compat)
|
| 569 | {
|
| 570 | struct mtd_part_parser *p, *ret = NULL;
|
| 571 |
|
| 572 | spin_lock(&part_parser_lock);
|
| 573 |
|
| 574 | list_for_each_entry(p, &part_parsers, list) {
|
| 575 | const struct of_device_id *matches;
|
| 576 |
|
| 577 | matches = p->of_match_table;
|
| 578 | if (!matches)
|
| 579 | continue;
|
| 580 |
|
| 581 | for (; matches->compatible[0]; matches++) {
|
| 582 | if (!strcmp(matches->compatible, compat) &&
|
| 583 | try_module_get(p->owner)) {
|
| 584 | ret = p;
|
| 585 | break;
|
| 586 | }
|
| 587 | }
|
| 588 |
|
| 589 | if (ret)
|
| 590 | break;
|
| 591 | }
|
| 592 |
|
| 593 | spin_unlock(&part_parser_lock);
|
| 594 |
|
| 595 | return ret;
|
| 596 | }
|
| 597 |
|
| 598 | static int mtd_part_of_parse(struct mtd_info *master,
|
| 599 | struct mtd_partitions *pparts)
|
| 600 | {
|
| 601 | struct mtd_part_parser *parser;
|
| 602 | struct device_node *np;
|
| 603 | struct device_node *child;
|
| 604 | struct property *prop;
|
| 605 | struct device *dev;
|
| 606 | const char *compat;
|
| 607 | const char *fixed = "fixed-partitions";
|
| 608 | int ret, err = 0;
|
| 609 |
|
| 610 | dev = &master->dev;
|
| 611 |
|
| 612 | if (!IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER) && !mtd_is_partition(master))
|
| 613 | dev = master->dev.parent;
|
| 614 |
|
| 615 | np = mtd_get_of_node(master);
|
| 616 | if (mtd_is_partition(master))
|
| 617 | of_node_get(np);
|
| 618 | else
|
| 619 | np = of_get_child_by_name(np, "partitions");
|
| 620 |
|
| 621 | |
| 622 | * Don't create devices that are added to a bus but will never get
|
| 623 | * probed. That'll cause fw_devlink to block probing of consumers of
|
| 624 | * this partition until the partition device is probed.
|
| 625 |
|
| 626 | for_each_child_of_node(np, child)
|
| 627 | if (of_device_is_compatible(child, "nvmem-cells"))
|
| 628 | of_node_set_flag(child, OF_POPULATED);
|
| 629 |
|
| 630 | of_property_for_each_string(np, "compatible", prop, compat) {
|
| 631 | parser = mtd_part_get_compatible_parser(compat);
|
| 632 | if (!parser)
|
| 633 | continue;
|
| 634 | ret = mtd_part_do_parse(parser, master, pparts, NULL);
|
| 635 | if (ret > 0) {
|
| 636 | of_platform_populate(np, NULL, NULL, dev);
|
| 637 | of_node_put(np);
|
| 638 | return ret;
|
| 639 | }
|
| 640 | mtd_part_parser_put(parser);
|
| 641 | if (ret < 0 && !err)
|
| 642 | err = ret;
|
| 643 | }
|
| 644 | of_platform_populate(np, NULL, NULL, dev);
|
| 645 | of_node_put(np);
|
| 646 |
|
| 647 | |
| 648 | * For backward compatibility we have to try the "fixed-partitions"
|
| 649 | * parser. It supports old DT format with partitions specified as a
|
| 650 | * direct subnodes of a flash device DT node without any compatibility
|
| 651 | * specified we could match.
|
| 652 |
|
| 653 | parser = mtd_part_parser_get(fixed);
|
| 654 | if (!parser && !request_module("%s", fixed))
|
| 655 | parser = mtd_part_parser_get(fixed);
|
| 656 | if (parser) {
|
| 657 | ret = mtd_part_do_parse(parser, master, pparts, NULL);
|
| 658 | if (ret > 0)
|
| 659 | return ret;
|
| 660 | mtd_part_parser_put(parser);
|
| 661 | if (ret < 0 && !err)
|
| 662 | err = ret;
|
| 663 | }
|
| 664 |
|
| 665 | return err;
|
| 666 | }
|
| 667 |
|
| 668 | |
| 669 | * parse_mtd_partitions - parse and register MTD partitions
|
| 670 | *
|
| 671 | * @master: the master partition (describes whole MTD device)
|
| 672 | * @types: names of partition parsers to try or %NULL
|
| 673 | * @data: MTD partition parser-specific data
|
| 674 | *
|
| 675 | * This function tries to find & register partitions on MTD device @master. It
|
| 676 | * uses MTD partition parsers, specified in @types. However, if @types is %NULL,
|
| 677 | * then the default list of parsers is used. The default list contains only the
|
| 678 | * "cmdlinepart" and "ofpart" parsers ATM.
|
| 679 | * Note: If there are more then one parser in @types, the kernel only takes the
|
| 680 | * partitions parsed out by the first parser.
|
| 681 | *
|
| 682 | * This function may return:
|
| 683 | * o a negative error code in case of failure
|
| 684 | * o number of found partitions otherwise
|
| 685 |
|
| 686 | int parse_mtd_partitions(struct mtd_info *master, const char *const *types,
|
| 687 | struct mtd_part_parser_data *data)
|
| 688 | {
|
| 689 | struct mtd_partitions pparts = { };
|
| 690 | struct mtd_part_parser *parser;
|
| 691 | int ret, err = 0;
|
| 692 |
|
| 693 | if (!types)
|
| 694 | types = mtd_is_partition(master) ? default_subpartition_types :
|
| 695 | default_mtd_part_types;
|
| 696 |
|
| 697 | for ( ; *types; types++) {
|
| 698 | |
| 699 | * ofpart is a special type that means OF partitioning info
|
| 700 | * should be used. It requires a bit different logic so it is
|
| 701 | * handled in a separated function.
|
| 702 |
|
| 703 | if (!strcmp(*types, "ofpart")) {
|
| 704 | ret = mtd_part_of_parse(master, &pparts);
|
| 705 | } else {
|
| 706 | pr_debug("%s: parsing partitions %s\n", master->name,
|
| 707 | *types);
|
| 708 | parser = mtd_part_parser_get(*types);
|
| 709 | if (!parser && !request_module("%s", *types))
|
| 710 | parser = mtd_part_parser_get(*types);
|
| 711 | if (!parser)
|
| 712 | continue;
|
| 713 | pr_debug("%s: got parser %s\n", master->name, parser->name);
|
| 714 | ret = mtd_part_do_parse(parser, master, &pparts, data);
|
| 715 | if (ret <= 0)
|
| 716 | mtd_part_parser_put(parser);
|
| 717 | }
|
| 718 |
|
| 719 | if (ret > 0) {
|
| 720 | err = add_mtd_partitions(master, pparts.parts,
|
| 721 | pparts.nr_parts);
|
| 722 | mtd_part_parser_cleanup(&pparts);
|
| 723 | return err ? err : pparts.nr_parts;
|
| 724 | }
|
| 725 | |
| 726 | * Stash the first error we see; only report it if no parser
|
| 727 | * succeeds
|
| 728 |
|
| 729 | if (ret < 0 && !err)
|
| 730 | err = ret;
|
| 731 | }
|
| 732 | return err;
|
| 733 | }
|
| 734 |
|
| 735 | void mtd_part_parser_cleanup(struct mtd_partitions *parts)
|
| 736 | {
|
| 737 | const struct mtd_part_parser *parser;
|
| 738 |
|
| 739 | if (!parts)
|
| 740 | return;
|
| 741 |
|
| 742 | parser = parts->parser;
|
| 743 | if (parser) {
|
| 744 | if (parser->cleanup)
|
| 745 | parser->cleanup(parts->parts, parts->nr_parts);
|
| 746 |
|
| 747 | mtd_part_parser_put(parser);
|
| 748 | }
|
| 749 | }
|
| 750 |
|
| 751 |
|
| 752 | uint64_t mtd_get_device_size(const struct mtd_info *mtd)
|
| 753 | {
|
| 754 | struct mtd_info *master = mtd_get_master((struct mtd_info *)mtd);
|
| 755 |
|
| 756 | return master->size;
|
| 757 | }
|
| 758 | EXPORT_SYMBOL_GPL(mtd_get_device_size);
|