2010-06-28 08:21:57 +00:00
|
|
|
#include <common.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <malloc.h>
|
|
|
|
#include <linux/err.h>
|
|
|
|
#include <linux/mtd/mtd.h>
|
|
|
|
|
|
|
|
static int mtd_part_read(struct mtd_info *mtd, loff_t from, size_t len,
|
|
|
|
size_t *retlen, u_char *buf)
|
|
|
|
{
|
|
|
|
int res;
|
|
|
|
|
|
|
|
if (from >= mtd->size)
|
|
|
|
len = 0;
|
|
|
|
else if (from + len > mtd->size)
|
|
|
|
len = mtd->size - from;
|
2014-01-14 08:33:28 +00:00
|
|
|
res = mtd->master->read(mtd->master, from + mtd->master_offset,
|
2010-06-28 08:21:57 +00:00
|
|
|
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)
|
|
|
|
{
|
|
|
|
if (!(mtd->flags & MTD_WRITEABLE))
|
|
|
|
return -EROFS;
|
|
|
|
if (to >= mtd->size)
|
|
|
|
len = 0;
|
|
|
|
else if (to + len > mtd->size)
|
|
|
|
len = mtd->size - to;
|
2014-01-14 08:33:28 +00:00
|
|
|
return mtd->master->write(mtd->master, to + mtd->master_offset,
|
2010-06-28 08:21:57 +00:00
|
|
|
len, retlen, buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int mtd_part_erase(struct mtd_info *mtd, struct erase_info *instr)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (!(mtd->flags & MTD_WRITEABLE))
|
|
|
|
return -EROFS;
|
|
|
|
if (instr->addr >= mtd->size)
|
|
|
|
return -EINVAL;
|
2014-01-14 08:33:28 +00:00
|
|
|
instr->addr += mtd->master_offset;
|
|
|
|
ret = mtd->master->erase(mtd->master, instr);
|
2010-06-28 08:21:57 +00:00
|
|
|
if (ret) {
|
|
|
|
if (instr->fail_addr != 0xffffffff)
|
2014-01-14 08:33:28 +00:00
|
|
|
instr->fail_addr -= mtd->master_offset;
|
|
|
|
instr->addr -= mtd->master_offset;
|
2010-06-28 08:21:57 +00:00
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int mtd_part_block_isbad(struct mtd_info *mtd, loff_t ofs)
|
|
|
|
{
|
|
|
|
if (ofs >= mtd->size)
|
|
|
|
return -EINVAL;
|
2014-01-14 08:33:28 +00:00
|
|
|
ofs += mtd->master_offset;
|
|
|
|
return mtd_block_isbad(mtd->master, ofs);
|
2010-06-28 08:21:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int mtd_part_block_markbad(struct mtd_info *mtd, loff_t ofs)
|
|
|
|
{
|
|
|
|
int res;
|
|
|
|
|
|
|
|
if (!(mtd->flags & MTD_WRITEABLE))
|
|
|
|
return -EROFS;
|
|
|
|
if (ofs >= mtd->size)
|
|
|
|
return -EINVAL;
|
2014-01-14 08:33:28 +00:00
|
|
|
ofs += mtd->master_offset;
|
|
|
|
res = mtd->master->block_markbad(mtd->master, ofs);
|
2010-06-28 08:21:57 +00:00
|
|
|
if (!res)
|
|
|
|
mtd->ecc_stats.badblocks++;
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2014-03-10 11:39:50 +00:00
|
|
|
struct mtd_info *mtd_add_partition(struct mtd_info *mtd, off_t offset,
|
|
|
|
uint64_t size, unsigned long flags, const char *name)
|
2010-06-28 08:21:57 +00:00
|
|
|
{
|
2014-01-14 08:33:28 +00:00
|
|
|
struct mtd_info *part;
|
2010-06-28 08:21:57 +00:00
|
|
|
|
2014-01-14 08:33:28 +00:00
|
|
|
part = xzalloc(sizeof(*part));
|
2010-06-28 08:21:57 +00:00
|
|
|
|
2014-01-14 09:30:20 +00:00
|
|
|
part->type = mtd->type;
|
|
|
|
part->flags = mtd->flags;
|
2014-02-13 13:25:47 +00:00
|
|
|
part->parent = &mtd->class_dev;
|
2014-01-14 09:30:20 +00:00
|
|
|
part->erasesize = mtd->erasesize;
|
|
|
|
part->writesize = mtd->writesize;
|
|
|
|
part->writebufsize = mtd->writebufsize;
|
|
|
|
part->oobsize = mtd->oobsize;
|
|
|
|
part->oobavail = mtd->oobavail;
|
|
|
|
part->bitflip_threshold = mtd->bitflip_threshold;
|
|
|
|
part->ecclayout = mtd->ecclayout;
|
|
|
|
part->ecc_strength = mtd->ecc_strength;
|
|
|
|
part->subpage_sft = mtd->subpage_sft;
|
2010-06-28 08:21:57 +00:00
|
|
|
|
2014-06-10 11:51:58 +00:00
|
|
|
if (mtd->numeraseregions > 1) {
|
|
|
|
/* Deal with variable erase size stuff */
|
|
|
|
int i, max = mtd->numeraseregions;
|
|
|
|
u64 end = offset + size;
|
|
|
|
struct mtd_erase_region_info *regions = mtd->eraseregions;
|
|
|
|
|
|
|
|
/* Find the first erase regions which is part of this
|
|
|
|
* partition. */
|
|
|
|
for (i = 0; i < max && regions[i].offset <= offset; i++)
|
|
|
|
;
|
|
|
|
/* The loop searched for the region _behind_ the first one */
|
|
|
|
if (i > 0)
|
|
|
|
i--;
|
|
|
|
|
|
|
|
/* Pick biggest erasesize */
|
|
|
|
for (; i < max && regions[i].offset < end; i++) {
|
|
|
|
if (part->erasesize < regions[i].erasesize) {
|
|
|
|
part->erasesize = regions[i].erasesize;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
BUG_ON(part->erasesize == 0);
|
|
|
|
} else {
|
|
|
|
/* Single erase size */
|
|
|
|
part->erasesize = mtd->erasesize;
|
2010-06-28 08:21:57 +00:00
|
|
|
}
|
|
|
|
|
2014-01-14 08:33:28 +00:00
|
|
|
part->read = mtd_part_read;
|
2014-05-13 07:33:27 +00:00
|
|
|
if (IS_ENABLED(CONFIG_MTD_WRITE)) {
|
|
|
|
part->write = mtd_part_write;
|
|
|
|
part->erase = mtd_part_erase;
|
|
|
|
part->block_markbad = mtd->block_markbad ? mtd_part_block_markbad : NULL;
|
|
|
|
}
|
|
|
|
|
2014-01-14 08:33:28 +00:00
|
|
|
part->block_isbad = mtd->block_isbad ? mtd_part_block_isbad : NULL;
|
|
|
|
part->size = size;
|
|
|
|
part->name = strdup(name);
|
2010-06-28 08:21:57 +00:00
|
|
|
|
2014-01-14 08:33:28 +00:00
|
|
|
part->master_offset = offset;
|
|
|
|
part->master = mtd;
|
2010-06-28 08:21:57 +00:00
|
|
|
|
2014-01-14 10:51:35 +00:00
|
|
|
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);
|
|
|
|
|
2014-01-14 08:33:28 +00:00
|
|
|
return part;
|
2010-06-28 08:21:57 +00:00
|
|
|
}
|
|
|
|
|
2014-01-14 08:33:28 +00:00
|
|
|
int mtd_del_partition(struct mtd_info *part)
|
2010-06-28 08:21:57 +00:00
|
|
|
{
|
2014-01-14 08:33:28 +00:00
|
|
|
if (!part->master)
|
|
|
|
return -EINVAL;
|
2010-06-28 08:21:57 +00:00
|
|
|
|
2014-01-14 10:51:35 +00:00
|
|
|
del_mtd_device(part);
|
|
|
|
|
|
|
|
free(part->cdev.partname);
|
2014-01-14 08:33:28 +00:00
|
|
|
free(part->name);
|
2010-06-28 08:21:57 +00:00
|
|
|
free(part);
|
2014-01-14 08:33:28 +00:00
|
|
|
|
|
|
|
return 0;
|
2010-06-28 08:21:57 +00:00
|
|
|
}
|