diff options
author | Sascha Hauer <s.hauer@pengutronix.de> | 2010-06-28 10:21:57 +0200 |
---|---|---|
committer | Sascha Hauer <s.hauer@pengutronix.de> | 2010-07-05 13:00:02 +0200 |
commit | acc46ca4f0e1332fb5dd47f4c24b4f71bdb99df5 (patch) | |
tree | 1461f8fef2a9f81c2dd1cbf0802d7fc1554a29fe /drivers | |
parent | a33bc77bddf4b01b42383b65ce97f51465d51a00 (diff) | |
download | barebox-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/Makefile | 1 | ||||
-rw-r--r-- | drivers/mtd/nand/nand.c | 2 | ||||
-rw-r--r-- | drivers/mtd/partition.c | 143 |
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); +} |