summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorSascha Hauer <s.hauer@pengutronix.de>2010-06-28 10:21:57 +0200
committerSascha Hauer <s.hauer@pengutronix.de>2010-07-05 13:00:02 +0200
commitacc46ca4f0e1332fb5dd47f4c24b4f71bdb99df5 (patch)
tree1461f8fef2a9f81c2dd1cbf0802d7fc1554a29fe /drivers
parenta33bc77bddf4b01b42383b65ce97f51465d51a00 (diff)
downloadbarebox-acc46ca4f0e1332fb5dd47f4c24b4f71bdb99df5.tar.gz
barebox-acc46ca4f0e1332fb5dd47f4c24b4f71bdb99df5.tar.xz
add partition mtd support
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/mtd/Makefile1
-rw-r--r--drivers/mtd/nand/nand.c2
-rw-r--r--drivers/mtd/partition.c143
3 files changed, 146 insertions, 0 deletions
diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile
index 87ee6f45d3..299cca13ad 100644
--- a/drivers/mtd/Makefile
+++ b/drivers/mtd/Makefile
@@ -1 +1,2 @@
obj-$(CONFIG_NAND) += nand/
+obj-$(CONFIG_PARTITION_NEED_MTD) += partition.o
diff --git a/drivers/mtd/nand/nand.c b/drivers/mtd/nand/nand.c
index 4927231c17..6a150fe0cb 100644
--- a/drivers/mtd/nand/nand.c
+++ b/drivers/mtd/nand/nand.c
@@ -123,6 +123,7 @@ static int nand_ioctl(struct cdev *cdev, int request, void *buf)
user->size = info->size;
user->erasesize = info->erasesize;
user->oobsize = info->oobsize;
+ user->mtd = info;
/* The below fields are obsolete */
user->ecctype = -1;
user->eccsize = 0;
@@ -220,6 +221,7 @@ int add_mtd_device(struct mtd_info *mtd)
mtd->cdev.name = asprintf("nand%d", mtd->class_dev.id);
mtd->cdev.priv = mtd;
mtd->cdev.dev = &mtd->class_dev;
+ mtd->cdev.mtd = mtd;
sprintf(str, "%u", mtd->size);
dev_add_param_fixed(&mtd->class_dev, "size", str);
diff --git a/drivers/mtd/partition.c b/drivers/mtd/partition.c
new file mode 100644
index 0000000000..df2eb40bf8
--- /dev/null
+++ b/drivers/mtd/partition.c
@@ -0,0 +1,143 @@
+#include <common.h>
+#include <errno.h>
+#include <malloc.h>
+#include <linux/err.h>
+#include <linux/mtd/mtd.h>
+
+struct mtd_part {
+ struct mtd_info mtd;
+ struct mtd_info *master;
+ uint64_t offset;
+ struct list_head list;
+};
+
+#define PART(x) ((struct mtd_part *)(x))
+
+static int mtd_part_read(struct mtd_info *mtd, loff_t from, size_t len,
+ size_t *retlen, u_char *buf)
+{
+ struct mtd_part *part = PART(mtd);
+ struct mtd_ecc_stats stats;
+ int res;
+
+ stats = part->master->ecc_stats;
+
+ if (from >= mtd->size)
+ len = 0;
+ else if (from + len > mtd->size)
+ len = mtd->size - from;
+ res = part->master->read(part->master, from + part->offset,
+ len, retlen, buf);
+ return res;
+}
+
+static int mtd_part_write(struct mtd_info *mtd, loff_t to, size_t len,
+ size_t *retlen, const u_char *buf)
+{
+ struct mtd_part *part = PART(mtd);
+
+ if (!(mtd->flags & MTD_WRITEABLE))
+ return -EROFS;
+ if (to >= mtd->size)
+ len = 0;
+ else if (to + len > mtd->size)
+ len = mtd->size - to;
+ return part->master->write(part->master, to + part->offset,
+ len, retlen, buf);
+}
+
+static int mtd_part_erase(struct mtd_info *mtd, struct erase_info *instr)
+{
+ struct mtd_part *part = PART(mtd);
+ int ret;
+
+ if (!(mtd->flags & MTD_WRITEABLE))
+ return -EROFS;
+ if (instr->addr >= mtd->size)
+ return -EINVAL;
+ instr->addr += part->offset;
+ ret = part->master->erase(part->master, instr);
+ if (ret) {
+ if (instr->fail_addr != 0xffffffff)
+ instr->fail_addr -= part->offset;
+ instr->addr -= part->offset;
+ }
+ return ret;
+}
+
+static int mtd_part_block_isbad(struct mtd_info *mtd, loff_t ofs)
+{
+ struct mtd_part *part = PART(mtd);
+ if (ofs >= mtd->size)
+ return -EINVAL;
+ ofs += part->offset;
+ return part->master->block_isbad(part->master, ofs);
+}
+
+static int mtd_part_block_markbad(struct mtd_info *mtd, loff_t ofs)
+{
+ struct mtd_part *part = PART(mtd);
+ int res;
+
+ if (!(mtd->flags & MTD_WRITEABLE))
+ return -EROFS;
+ if (ofs >= mtd->size)
+ return -EINVAL;
+ ofs += part->offset;
+ res = part->master->block_markbad(part->master, ofs);
+ if (!res)
+ mtd->ecc_stats.badblocks++;
+ return res;
+}
+
+struct mtd_info *mtd_add_partition(struct mtd_info *mtd, off_t offset, size_t size,
+ unsigned long flags, const char *name)
+{
+ struct mtd_part *slave;
+ struct mtd_info *slave_mtd;
+ int start = 0, end = 0, i;
+
+ slave = xzalloc(sizeof(*slave));
+ slave_mtd = &slave->mtd;
+
+ memcpy(slave_mtd, mtd, sizeof(*slave));
+
+ /*
+ * find the number of eraseregions the partition includes.
+ * Do not bother to create the mtd_erase_region_infos as
+ * ubi is only interested in its number. UBI does not
+ * yet support multiple erase regions.
+ */
+ for (i = mtd->numeraseregions - 1; i >= 0; i--) {
+ struct mtd_erase_region_info *region = &mtd->eraseregions[i];
+ if (offset >= region->offset &&
+ offset < region->offset + region->erasesize * region->numblocks)
+ start = i;
+ if (offset + size >= region->offset &&
+ offset + size <= region->offset + region->erasesize * region->numblocks)
+ end = i;
+ }
+
+ slave_mtd->numeraseregions = end - start;
+
+ slave_mtd->read = mtd_part_read;
+ slave_mtd->write = mtd_part_write;
+ slave_mtd->erase = mtd_part_erase;
+ slave_mtd->block_isbad = mtd->block_isbad ? mtd_part_block_isbad : NULL;
+ slave_mtd->block_markbad = mtd->block_markbad ? mtd_part_block_markbad : NULL;
+ slave_mtd->size = size;
+ slave_mtd->name = strdup(name);
+
+ slave->offset = offset;
+ slave->master = mtd;
+
+ return slave_mtd;
+}
+
+void mtd_del_partition(struct mtd_info *mtd)
+{
+ struct mtd_part *part = PART(mtd);
+
+ free(mtd->name);
+ free(part);
+}