summaryrefslogtreecommitdiffstats
path: root/drivers/mtd
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mtd')
-rw-r--r--drivers/mtd/core.c97
1 files changed, 96 insertions, 1 deletions
diff --git a/drivers/mtd/core.c b/drivers/mtd/core.c
index 99d50f147c..ac1001e05f 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,6 +378,98 @@ 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);
@@ -448,8 +541,10 @@ int add_mtd_device(struct mtd_info *mtd, char *devname, int device_id)
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);
+ }
list_for_each_entry(hook, &mtd_register_hooks, hook)
if (hook->add_mtd_device)