diff options
author | Sascha Hauer <s.hauer@pengutronix.de> | 2015-03-06 08:33:43 +0100 |
---|---|---|
committer | Sascha Hauer <s.hauer@pengutronix.de> | 2015-03-06 08:33:43 +0100 |
commit | 0e055c254c3eab40b52b3ac5f845a889bd281e05 (patch) | |
tree | 4f849bb3d50194ebc091b8be25b1b5072d73d063 /drivers | |
parent | 9524e7d0d7d22fbc9bde4fcebc7eeb6a12763c12 (diff) | |
parent | fba2adcb3d50e86d4e26da9126c1ef6424e348e6 (diff) | |
download | barebox-0e055c254c3eab40b52b3ac5f845a889bd281e05.tar.gz barebox-0e055c254c3eab40b52b3ac5f845a889bd281e05.tar.xz |
Merge branch 'for-next/mtd-partitions'
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/mtd/core.c | 213 | ||||
-rw-r--r-- | drivers/mtd/nand/nand-bb.c | 16 | ||||
-rw-r--r-- | drivers/mtd/partition.c | 14 |
3 files changed, 234 insertions, 9 deletions
diff --git a/drivers/mtd/core.c b/drivers/mtd/core.c index 345752ecfe..63b1e4a824 100644 --- a/drivers/mtd/core.c +++ b/drivers/mtd/core.c @@ -18,6 +18,7 @@ #include <common.h> #include <linux/mtd/nand.h> #include <linux/mtd/mtd.h> +#include <cmdlinepart.h> #include <init.h> #include <xfuncs.h> #include <driver.h> @@ -377,9 +378,174 @@ static struct file_operations mtd_ops = { .lseek = dev_lseek_default, }; +static int mtd_partition_set(struct device_d *dev, struct param_d *p, const char *val) +{ + struct mtd_info *mtd = container_of(dev, struct mtd_info, class_dev); + struct mtd_info *mtdpart, *tmp; + int ret; + + list_for_each_entry_safe(mtdpart, tmp, &mtd->partitions, partitions_entry) { + ret = mtd_del_partition(mtdpart); + if (ret) + return ret; + } + + return cmdlinepart_do_parse(mtd->cdev.name, val, mtd->size, CMDLINEPART_ADD_DEVNAME); +} + +static char *print_size(uint64_t s) +{ + if (!(s & ((1 << 20) - 1))) + return asprintf("%lldM", s >> 20); + if (!(s & ((1 << 10) - 1))) + return asprintf("%lldk", s >> 10); + return asprintf("0x%lld", s); +} + +static int print_part(char *buf, int bufsize, struct mtd_info *mtd, uint64_t last_ofs, + int is_last) +{ + char *size = print_size(mtd->size); + char *ofs = print_size(mtd->master_offset); + int ret; + + if (!size || !ofs) { + ret = -ENOMEM; + goto out; + } + + if (mtd->master_offset == last_ofs) + ret = snprintf(buf, bufsize, "%s(%s)%s", size, + mtd->cdev.partname, + is_last ? "" : ","); + else + ret = snprintf(buf, bufsize, "%s@%s(%s)%s", size, + ofs, + mtd->cdev.partname, + is_last ? "" : ","); +out: + free(size); + free(ofs); + + return ret; +} + +static int print_parts(char *buf, int bufsize, struct mtd_info *mtd) +{ + struct mtd_info *mtdpart; + uint64_t last_ofs = 0; + int ret = 0; + + list_for_each_entry(mtdpart, &mtd->partitions, partitions_entry) { + int now; + int is_last = list_is_last(&mtdpart->partitions_entry, + &mtd->partitions); + + now = print_part(buf, bufsize, mtdpart, last_ofs, is_last); + if (now < 0) + return now; + + if (buf && bufsize) { + buf += now; + bufsize -= now; + } + ret += now; + last_ofs = mtdpart->master_offset + mtdpart->size; + } + + return ret; +} + +static const char *mtd_partition_get(struct device_d *dev, struct param_d *p) +{ + struct mtd_info *mtd = container_of(dev, struct mtd_info, class_dev); + int len = 0; + + free(p->value); + + len = print_parts(NULL, 0, mtd); + p->value = xzalloc(len + 1); + print_parts(p->value, len + 1, mtd); + + return p->value; +} + +static int mtd_part_compare(struct list_head *a, struct list_head *b) +{ + struct mtd_info *mtda = container_of(a, struct mtd_info, partitions_entry); + struct mtd_info *mtdb = container_of(b, struct mtd_info, partitions_entry); + + if (mtda->master_offset > mtdb->master_offset) + return 1; + if (mtda->master_offset < mtdb->master_offset) + return -1; + return 0; +} + +static int of_mtd_fixup(struct device_node *root, void *ctx) +{ + struct mtd_info *mtd = ctx, *partmtd; + struct device_node *np, *part, *tmp; + int ret, i = 0; + + np = of_find_node_by_path(mtd->of_path); + if (!np) { + dev_err(&mtd->class_dev, "Cannot find nodepath %s, cannot fixup\n", + mtd->of_path); + return -EINVAL; + } + + for_each_child_of_node_safe(np, tmp, part) { + if (of_get_property(part, "compatible", NULL)) + continue; + of_delete_node(part); + } + + list_for_each_entry(partmtd, &mtd->partitions, partitions_entry) { + int na, ns, len = 0; + char *name = asprintf("partition@%d", i++); + void *p; + u8 tmp[16 * 16]; /* Up to 64-bit address + 64-bit size */ + + if (!name) + return -ENOMEM; + + part = of_new_node(np, name); + free(name); + if (!part) + return -ENOMEM; + + p = of_new_property(part, "label", partmtd->cdev.partname, + strlen(partmtd->cdev.partname) + 1); + if (!p) + return -ENOMEM; + + na = of_n_addr_cells(np); + ns = of_n_size_cells(np); + + of_write_number(tmp + len, partmtd->master_offset, na); + len += na * 4; + of_write_number(tmp + len, partmtd->size, ns); + len += ns * 4; + + ret = of_set_property(part, "reg", tmp, len, 1); + if (ret) + return ret; + + if (partmtd->cdev.flags & DEVFS_PARTITION_READONLY) { + ret = of_set_property(part, "read-only", NULL, 0, 1); + if (ret) + return ret; + } + } + + return 0; +} + int add_mtd_device(struct mtd_info *mtd, char *devname, int device_id) { struct mtddev_hook *hook; + int ret; if (!devname) devname = "mtd"; @@ -387,7 +553,10 @@ int add_mtd_device(struct mtd_info *mtd, char *devname, int device_id) mtd->class_dev.id = device_id; if (mtd->parent) mtd->class_dev.parent = mtd->parent; - register_device(&mtd->class_dev); + + ret = register_device(&mtd->class_dev); + if (ret) + return ret; mtd->cdev.ops = &mtd_ops; mtd->cdev.size = mtd->size; @@ -396,6 +565,8 @@ int add_mtd_device(struct mtd_info *mtd, char *devname, int device_id) else mtd->cdev.name = asprintf("%s%d", devname, mtd->class_dev.id); + INIT_LIST_HEAD(&mtd->partitions); + mtd->cdev.priv = mtd; mtd->cdev.dev = &mtd->class_dev; mtd->cdev.mtd = mtd; @@ -407,19 +578,50 @@ int add_mtd_device(struct mtd_info *mtd, char *devname, int device_id) dev_add_param_int_ro(&mtd->class_dev, "oobsize", mtd->oobsize, "%u"); } - devfs_create(&mtd->cdev); + ret = devfs_create(&mtd->cdev); + if (ret) + goto err; + + if (mtd->master && !(mtd->cdev.flags & DEVFS_PARTITION_FIXED)) { + struct mtd_info *mtdpart; + + list_for_each_entry(mtdpart, &mtd->master->partitions, partitions_entry) { + if (mtdpart->master_offset + mtdpart->size <= mtd->master_offset) + continue; + if (mtd->master_offset + mtd->size <= mtdpart->master_offset) + continue; + dev_err(&mtd->class_dev, "New partition %s conflicts with %s\n", + mtd->name, mtdpart->name); + goto err1; + } + + list_add_sort(&mtd->partitions_entry, &mtd->master->partitions, mtd_part_compare); + } if (mtd_can_have_bb(mtd)) mtd->cdev_bb = mtd_add_bb(mtd, NULL); - if (mtd->parent && !mtd->master) + if (mtd->parent && !mtd->master) { + dev_add_param(&mtd->class_dev, "partitions", mtd_partition_set, mtd_partition_get, 0); of_parse_partitions(&mtd->cdev, mtd->parent->device_node); + if (IS_ENABLED(CONFIG_OFDEVICE) && mtd->parent->device_node) { + mtd->of_path = xstrdup(mtd->parent->device_node->full_name); + of_register_fixup(of_mtd_fixup, mtd); + } + } list_for_each_entry(hook, &mtd_register_hooks, hook) if (hook->add_mtd_device) hook->add_mtd_device(mtd, devname, &hook->priv); return 0; +err1: + devfs_remove(&mtd->cdev); +err: + free(mtd->cdev.name); + unregister_device(&mtd->class_dev); + + return ret; } int del_mtd_device (struct mtd_info *mtd) @@ -431,9 +633,14 @@ int del_mtd_device (struct mtd_info *mtd) hook->del_mtd_device(mtd, &hook->priv); devfs_remove(&mtd->cdev); + if (mtd->cdev_bb) + mtd_del_bb(mtd); unregister_device(&mtd->class_dev); free(mtd->param_size.value); free(mtd->cdev.name); + if (mtd->master) + list_del(&mtd->partitions_entry); + return 0; } diff --git a/drivers/mtd/nand/nand-bb.c b/drivers/mtd/nand/nand-bb.c index 23957c358e..539c685482 100644 --- a/drivers/mtd/nand/nand-bb.c +++ b/drivers/mtd/nand/nand-bb.c @@ -307,6 +307,17 @@ err: return ERR_PTR(ret); } +void mtd_del_bb(struct mtd_info *mtd) +{ + struct cdev *cdev = mtd->cdev_bb; + struct nand_bb *bb = container_of(cdev, struct nand_bb, cdev); + + devfs_remove(&bb->cdev); + list_del_init(&bb->list); + free(bb->name); + free(bb); +} + /** * Add a bad block aware device ontop of another (NAND) device * @param[in] dev The device to add a partition on @@ -335,10 +346,7 @@ int dev_remove_bb_dev(const char *name) list_for_each_entry_safe(bb, tmp, &bb_list, list) { if (!strcmp(bb->cdev.name, name)) { - devfs_remove(&bb->cdev); - list_del_init(&bb->list); - free(bb->name); - free(bb); + mtd_del_bb(bb->mtd); return 0; } } diff --git a/drivers/mtd/partition.c b/drivers/mtd/partition.c index e393fa68f8..98b90819bd 100644 --- a/drivers/mtd/partition.c +++ b/drivers/mtd/partition.c @@ -108,6 +108,7 @@ struct mtd_info *mtd_add_partition(struct mtd_info *mtd, off_t offset, uint64_t size, unsigned long flags, const char *name) { struct mtd_info *part; + int ret; part = xzalloc(sizeof(*part)); @@ -122,6 +123,7 @@ struct mtd_info *mtd_add_partition(struct mtd_info *mtd, off_t offset, part->ecclayout = mtd->ecclayout; part->ecc_strength = mtd->ecc_strength; part->subpage_sft = mtd->subpage_sft; + part->cdev.flags = flags; if (mtd->numeraseregions > 1) { /* Deal with variable erase size stuff */ @@ -160,7 +162,7 @@ struct mtd_info *mtd_add_partition(struct mtd_info *mtd, off_t offset, part->block_isbad = mtd->block_isbad ? mtd_part_block_isbad : NULL; part->size = size; - part->name = strdup(name); + part->name = xstrdup(name); part->master_offset = offset; part->master = mtd; @@ -168,9 +170,17 @@ struct mtd_info *mtd_add_partition(struct mtd_info *mtd, off_t offset, if (!strncmp(mtd->cdev.name, name, strlen(mtd->cdev.name))) part->cdev.partname = xstrdup(name + strlen(mtd->cdev.name) + 1); - add_mtd_device(part, part->name, DEVICE_ID_SINGLE); + ret = add_mtd_device(part, part->name, DEVICE_ID_SINGLE); + if (ret) + goto err; return part; +err: + free(part->cdev.partname); + free(part->name); + free(part); + + return ERR_PTR(ret); } int mtd_del_partition(struct mtd_info *part) |