Commit dfe64e2c authored by Sergey Lapin's avatar Sergey Lapin Committed by Scott Wood

mtd: resync with Linux-3.7.1

This patch is essentially an update of u-boot MTD subsystem to
the state of Linux-3.7.1 with exclusion of some bits:

- the update is concentrated on NAND, no onenand or CFI/NOR/SPI
flashes interfaces are updated EXCEPT for API changes.

- new large NAND chips support is there, though some updates
have got in Linux-3.8.-rc1, (which will follow on top of this patch).

To produce this update I used tag v3.7.1 of linux-stable repository.

The update was made using application of relevant patches,
with changes relevant to U-Boot-only stuff sticked together
to keep bisectability. Then all changes were grouped together
to this patch.
Signed-off-by: default avatarSergey Lapin <slapin@ossfans.org>
[scottwood@freescale.com: some eccstrength and build fixes]
Signed-off-by: default avatarScott Wood <scottwood@freescale.com>
parent a1b81ab2
......@@ -120,7 +120,7 @@ int board_eth_init(bd_t *bis)
#ifdef CONFIG_NAND_DAVINCI
static int
davinci_std_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
uint8_t *buf, int page)
uint8_t *buf, int oob_required, int page)
{
struct nand_chip *this = mtd->priv;
int i, eccsize = chip->ecc.size;
......@@ -167,8 +167,9 @@ davinci_std_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
return 0;
}
static void davinci_std_write_page_syndrome(struct mtd_info *mtd,
struct nand_chip *chip, const uint8_t *buf)
static int davinci_std_write_page_syndrome(struct mtd_info *mtd,
struct nand_chip *chip, const uint8_t *buf,
int oob_required)
{
unsigned char davinci_ecc_buf[NAND_MAX_OOBSIZE];
struct nand_chip *this = mtd->priv;
......@@ -218,6 +219,7 @@ static void davinci_std_write_page_syndrome(struct mtd_info *mtd,
i = mtd->oobsize - (oob - chip->oob_poi);
if (i)
chip->write_buf(mtd, oob, i);
return 0;
}
static int davinci_std_write_oob_syndrome(struct mtd_info *mtd,
......@@ -239,7 +241,7 @@ static int davinci_std_write_oob_syndrome(struct mtd_info *mtd,
}
static int davinci_std_read_oob_syndrome(struct mtd_info *mtd,
struct nand_chip *chip, int page, int sndcmd)
struct nand_chip *chip, int page)
{
struct nand_chip *this = mtd->priv;
uint8_t *buf = chip->oob_poi;
......@@ -249,7 +251,7 @@ static int davinci_std_read_oob_syndrome(struct mtd_info *mtd,
chip->read_buf(mtd, bufpoi, mtd->oobsize);
return 1;
return 0;
}
static void nand_dm365evm_select_chip(struct mtd_info *mtd, int chip)
......
......@@ -62,8 +62,8 @@ static int nand_dump(nand_info_t *nand, ulong off, int only_oob, int repeat)
ops.oobbuf = oobbuf;
ops.len = nand->writesize;
ops.ooblen = nand->oobsize;
ops.mode = MTD_OOB_RAW;
i = nand->read_oob(nand, addr, &ops);
ops.mode = MTD_OPS_RAW;
i = mtd_read_oob(nand, addr, &ops);
if (i < 0) {
printf("Error (%d) reading page %08lx\n", i, off);
free(datbuf);
......@@ -404,13 +404,13 @@ static int raw_access(nand_info_t *nand, ulong addr, loff_t off, ulong count,
.oobbuf = ((u8 *)addr) + nand->writesize,
.len = nand->writesize,
.ooblen = nand->oobsize,
.mode = MTD_OOB_RAW
.mode = MTD_OPS_RAW
};
if (read)
ret = nand->read_oob(nand, off, &ops);
ret = mtd_read_oob(nand, off, &ops);
else
ret = nand->write_oob(nand, off, &ops);
ret = mtd_write_oob(nand, off, &ops);
if (ret) {
printf("%s: error at offset %llx, ret %d\n",
......@@ -715,13 +715,13 @@ static int do_nand(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
mtd_oob_ops_t ops = {
.oobbuf = (u8 *)addr,
.ooblen = rwsize,
.mode = MTD_OOB_RAW
.mode = MTD_OPS_RAW
};
if (read)
ret = nand->read_oob(nand, off, &ops);
ret = mtd_read_oob(nand, off, &ops);
else
ret = nand->write_oob(nand, off, &ops);
ret = mtd_write_oob(nand, off, &ops);
} else if (raw) {
ret = raw_access(nand, addr, off, pagecount, read);
} else {
......@@ -764,7 +764,7 @@ static int do_nand(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
while (argc > 0) {
addr = simple_strtoul(*argv, NULL, 16);
if (nand->block_markbad(nand, addr)) {
if (mtd_block_markbad(nand, addr)) {
printf("block 0x%08lx NOT marked "
"as bad! ERROR %d\n",
addr, ret);
......
......@@ -83,7 +83,7 @@ static int onenand_block_read(loff_t from, size_t len,
ops.len = blocksize;
while (blocks) {
ret = mtd->block_isbad(mtd, ofs);
ret = mtd_block_isbad(mtd, ofs);
if (ret) {
printk("Bad blocks %d at 0x%x\n",
(u32)(ofs >> this->erase_shift), (u32)ofs);
......@@ -97,7 +97,7 @@ static int onenand_block_read(loff_t from, size_t len,
ops.datbuf = buf;
ops.retlen = 0;
ret = mtd->read_oob(mtd, ofs, &ops);
ret = mtd_read_oob(mtd, ofs, &ops);
if (ret) {
printk("Read failed 0x%x, %d\n", (u32)ofs, ret);
ofs += blocksize;
......@@ -118,7 +118,7 @@ static int onenand_write_oneblock_withoob(loff_t to, const u_char * buf,
struct mtd_oob_ops ops = {
.len = mtd->writesize,
.ooblen = mtd->oobsize,
.mode = MTD_OOB_AUTO,
.mode = MTD_OPS_AUTO_OOB,
};
int page, ret = 0;
for (page = 0; page < (mtd->erasesize / mtd->writesize); page ++) {
......@@ -126,7 +126,7 @@ static int onenand_write_oneblock_withoob(loff_t to, const u_char * buf,
buf += mtd->writesize;
ops.oobbuf = (u_char *)buf;
buf += mtd->oobsize;
ret = mtd->write_oob(mtd, to, &ops);
ret = mtd_write_oob(mtd, to, &ops);
if (ret)
break;
to += mtd->writesize;
......@@ -156,7 +156,7 @@ static int onenand_block_write(loff_t to, size_t len,
ofs = to;
while (blocks) {
ret = mtd->block_isbad(mtd, ofs);
ret = mtd_block_isbad(mtd, ofs);
if (ret) {
printk("Bad blocks %d at 0x%x\n",
(u32)(ofs >> this->erase_shift), (u32)ofs);
......@@ -165,7 +165,7 @@ static int onenand_block_write(loff_t to, size_t len,
}
if (!withoob)
ret = mtd->write(mtd, ofs, blocksize, &_retlen, buf);
ret = mtd_write(mtd, ofs, blocksize, &_retlen, buf);
else
ret = onenand_write_oneblock_withoob(ofs, buf, &_retlen);
if (ret) {
......@@ -195,7 +195,7 @@ static int onenand_block_erase(u32 start, u32 size, int force)
int blocksize = 1 << this->erase_shift;
for (ofs = start; ofs < (start + size); ofs += blocksize) {
ret = mtd->block_isbad(mtd, ofs);
ret = mtd_block_isbad(mtd, ofs);
if (ret && !force) {
printf("Skip erase bad block %d at 0x%x\n",
(u32)(ofs >> this->erase_shift), (u32)ofs);
......@@ -206,7 +206,7 @@ static int onenand_block_erase(u32 start, u32 size, int force)
instr.len = blocksize;
instr.priv = force;
instr.mtd = mtd;
ret = mtd->erase(mtd, &instr);
ret = mtd_erase(mtd, &instr);
if (ret) {
printf("erase failed block %d at 0x%x\n",
(u32)(ofs >> this->erase_shift), (u32)ofs);
......@@ -261,7 +261,7 @@ static int onenand_block_test(u32 start, u32 size)
while (blocks < end_block) {
printf("\rTesting block %d at 0x%x", (u32)(ofs >> this->erase_shift), (u32)ofs);
ret = mtd->block_isbad(mtd, ofs);
ret = mtd_block_isbad(mtd, ofs);
if (ret) {
printf("Skip erase bad block %d at 0x%x\n",
(u32)(ofs >> this->erase_shift), (u32)ofs);
......@@ -270,19 +270,19 @@ static int onenand_block_test(u32 start, u32 size)
instr.addr = ofs;
instr.len = blocksize;
ret = mtd->erase(mtd, &instr);
ret = mtd_erase(mtd, &instr);
if (ret) {
printk("Erase failed 0x%x, %d\n", (u32)ofs, ret);
goto next;
}
ret = mtd->write(mtd, ofs, blocksize, &retlen, buf);
ret = mtd_write(mtd, ofs, blocksize, &retlen, buf);
if (ret) {
printk("Write failed 0x%x, %d\n", (u32)ofs, ret);
goto next;
}
ret = mtd->read(mtd, ofs, blocksize, &retlen, verify_buf);
ret = mtd_read(mtd, ofs, blocksize, &retlen, verify_buf);
if (ret) {
printk("Read failed 0x%x, %d\n", (u32)ofs, ret);
goto next;
......@@ -324,7 +324,7 @@ static int onenand_dump(struct mtd_info *mtd, ulong off, int only_oob)
ops.len = mtd->writesize;
ops.ooblen = mtd->oobsize;
ops.retlen = 0;
i = mtd->read_oob(mtd, addr, &ops);
i = mtd_read_oob(mtd, addr, &ops);
if (i < 0) {
printf("Error (%d) reading page %08lx\n", i, off);
free(datbuf);
......@@ -373,7 +373,7 @@ static int do_onenand_bad(cmd_tbl_t * cmdtp, int flag, int argc, char * const ar
/* Currently only one OneNAND device is supported */
printf("\nDevice %d bad blocks:\n", 0);
for (ofs = 0; ofs < mtd->size; ofs += mtd->erasesize) {
if (mtd->block_isbad(mtd, ofs))
if (mtd_block_isbad(mtd, ofs))
printf(" %08x\n", (u32)ofs);
}
......@@ -530,7 +530,7 @@ static int do_onenand_markbad(cmd_tbl_t * cmdtp, int flag, int argc, char * cons
while (argc > 0) {
addr = simple_strtoul(*argv, NULL, 16);
if (mtd->block_markbad(mtd, addr)) {
if (mtd_block_markbad(mtd, addr)) {
printf("block 0x%08lx NOT marked "
"as bad! ERROR %d\n",
addr, ret);
......
......@@ -68,7 +68,7 @@ void env_relocate_spec(void)
/* Check OneNAND exist */
if (mtd->writesize)
/* Ignore read fail */
mtd->read(mtd, env_addr, ONENAND_MAX_ENV_SIZE,
mtd_read(mtd, env_addr, ONENAND_MAX_ENV_SIZE,
&retlen, (u_char *)buf);
else
mtd->writesize = MAX_ONENAND_PAGESIZE;
......@@ -113,12 +113,12 @@ int saveenv(void)
#endif
instr.addr = env_addr;
instr.mtd = mtd;
if (mtd->erase(mtd, &instr)) {
if (mtd_erase(mtd, &instr)) {
printf("OneNAND: erase failed at 0x%08llx\n", env_addr);
return 1;
}
if (mtd->write(mtd, env_addr, ONENAND_MAX_ENV_SIZE, &retlen,
if (mtd_write(mtd, env_addr, ONENAND_MAX_ENV_SIZE, &retlen,
(u_char *)&env_new)) {
printf("OneNAND: write failed at 0x%llx\n", instr.addr);
return 2;
......
......@@ -25,7 +25,9 @@ include $(TOPDIR)/config.mk
LIB := $(obj)libmtd.o
COBJS-$(CONFIG_MTD_DEVICE) += mtdcore.o
ifneq (,$(findstring y,$(CONFIG_MTD_DEVICE)$(CONFIG_CMD_NAND)$(CONFIG_CMD_ONENAND)))
COBJS-y += mtdcore.o
endif
COBJS-$(CONFIG_MTD_PARTITIONS) += mtdpart.o
COBJS-$(CONFIG_MTD_CONCAT) += mtdconcat.o
COBJS-$(CONFIG_HAS_DATAFLASH) += at45.o
......
......@@ -244,12 +244,12 @@ int cfi_mtd_init(void)
mtd->size = fi->size;
mtd->writesize = 1;
mtd->erase = cfi_mtd_erase;
mtd->read = cfi_mtd_read;
mtd->write = cfi_mtd_write;
mtd->sync = cfi_mtd_sync;
mtd->lock = cfi_mtd_lock;
mtd->unlock = cfi_mtd_unlock;
mtd->_erase = cfi_mtd_erase;
mtd->_read = cfi_mtd_read;
mtd->_write = cfi_mtd_write;
mtd->_sync = cfi_mtd_sync;
mtd->_lock = cfi_mtd_lock;
mtd->_unlock = cfi_mtd_unlock;
mtd->priv = fi;
if (add_mtd_device(mtd))
......
......@@ -70,14 +70,14 @@ concat_read(struct mtd_info *mtd, loff_t from, size_t len,
/* Entire transaction goes into this subdev */
size = len;
err = subdev->read(subdev, from, size, &retsize, buf);
err = mtd_read(subdev, from, size, &retsize, buf);
/* Save information about bitflips! */
if (unlikely(err)) {
if (err == -EBADMSG) {
if (mtd_is_eccerr(err)) {
mtd->ecc_stats.failed++;
ret = err;
} else if (err == -EUCLEAN) {
} else if (mtd_is_bitflip(err)) {
mtd->ecc_stats.corrected++;
/* Do not overwrite -EBADMSG !! */
if (!ret)
......@@ -105,9 +105,6 @@ concat_write(struct mtd_info *mtd, loff_t to, size_t len,
int err = -EINVAL;
int i;
if (!(mtd->flags & MTD_WRITEABLE))
return -EROFS;
*retlen = 0;
for (i = 0; i < concat->num_subdev; i++) {
......@@ -124,11 +121,7 @@ concat_write(struct mtd_info *mtd, loff_t to, size_t len,
else
size = len;
if (!(subdev->flags & MTD_WRITEABLE))
err = -EROFS;
else
err = subdev->write(subdev, to, size, &retsize, buf);
err = mtd_write(subdev, to, size, &retsize, buf);
if (err)
break;
......@@ -165,16 +158,16 @@ concat_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops)
if (from + devops.len > subdev->size)
devops.len = subdev->size - from;
err = subdev->read_oob(subdev, from, &devops);
err = mtd_read_oob(subdev, from, &devops);
ops->retlen += devops.retlen;
ops->oobretlen += devops.oobretlen;
/* Save information about bitflips! */
if (unlikely(err)) {
if (err == -EBADMSG) {
if (mtd_is_eccerr(err)) {
mtd->ecc_stats.failed++;
ret = err;
} else if (err == -EUCLEAN) {
} else if (mtd_is_bitflip(err)) {
mtd->ecc_stats.corrected++;
/* Do not overwrite -EBADMSG !! */
if (!ret)
......@@ -225,7 +218,7 @@ concat_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops)
if (to + devops.len > subdev->size)
devops.len = subdev->size - to;
err = subdev->write_oob(subdev, to, &devops);
err = mtd_write_oob(subdev, to, &devops);
ops->retlen += devops.retlen;
if (err)
return err;
......@@ -271,7 +264,7 @@ static int concat_dev_erase(struct mtd_info *mtd, struct erase_info *erase)
* FIXME: Allow INTERRUPTIBLE. Which means
* not having the wait_queue head on the stack.
*/
err = mtd->erase(mtd, erase);
err = mtd_erase(mtd, erase);
if (!err) {
set_current_state(TASK_UNINTERRUPTIBLE);
add_wait_queue(&waitq, &wait);
......@@ -294,15 +287,6 @@ static int concat_erase(struct mtd_info *mtd, struct erase_info *instr)
uint64_t length, offset = 0;
struct erase_info *erase;
if (!(mtd->flags & MTD_WRITEABLE))
return -EROFS;
if (instr->addr > concat->mtd.size)
return -EINVAL;
if (instr->len + instr->addr > concat->mtd.size)
return -EINVAL;
/*
* Check for proper erase block alignment of the to-be-erased area.
* It is easier to do this based on the super device's erase
......@@ -350,8 +334,6 @@ static int concat_erase(struct mtd_info *mtd, struct erase_info *instr)
return -EINVAL;
}
instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN;
/* make a local copy of instr to avoid modifying the caller's struct */
erase = kmalloc(sizeof (struct erase_info), GFP_KERNEL);
......@@ -390,10 +372,6 @@ static int concat_erase(struct mtd_info *mtd, struct erase_info *instr)
else
erase->len = length;
if (!(subdev->flags & MTD_WRITEABLE)) {
err = -EROFS;
break;
}
length -= erase->len;
if ((err = concat_dev_erase(subdev, erase))) {
/* sanity check: should never happen since
......@@ -429,9 +407,6 @@ static int concat_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
struct mtd_concat *concat = CONCAT(mtd);
int i, err = -EINVAL;
if ((len + ofs) > mtd->size)
return -EINVAL;
for (i = 0; i < concat->num_subdev; i++) {
struct mtd_info *subdev = concat->subdev[i];
uint64_t size;
......@@ -446,7 +421,7 @@ static int concat_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
else
size = len;
err = subdev->lock(subdev, ofs, size);
err = mtd_lock(subdev, ofs, size);
if (err)
break;
......@@ -467,9 +442,6 @@ static int concat_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
struct mtd_concat *concat = CONCAT(mtd);
int i, err = 0;
if ((len + ofs) > mtd->size)
return -EINVAL;
for (i = 0; i < concat->num_subdev; i++) {
struct mtd_info *subdev = concat->subdev[i];
uint64_t size;
......@@ -484,7 +456,7 @@ static int concat_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
else
size = len;
err = subdev->unlock(subdev, ofs, size);
err = mtd_unlock(subdev, ofs, size);
if (err)
break;
......@@ -507,7 +479,7 @@ static void concat_sync(struct mtd_info *mtd)
for (i = 0; i < concat->num_subdev; i++) {
struct mtd_info *subdev = concat->subdev[i];
subdev->sync(subdev);
mtd_sync(subdev);
}
}
......@@ -516,12 +488,9 @@ static int concat_block_isbad(struct mtd_info *mtd, loff_t ofs)
struct mtd_concat *concat = CONCAT(mtd);
int i, res = 0;
if (!concat->subdev[0]->block_isbad)
if (!mtd_can_have_bb(concat->subdev[0]))
return res;
if (ofs > mtd->size)
return -EINVAL;
for (i = 0; i < concat->num_subdev; i++) {
struct mtd_info *subdev = concat->subdev[i];
......@@ -530,7 +499,7 @@ static int concat_block_isbad(struct mtd_info *mtd, loff_t ofs)
continue;
}
res = subdev->block_isbad(subdev, ofs);
res = mtd_block_isbad(subdev, ofs);
break;
}
......@@ -542,12 +511,9 @@ static int concat_block_markbad(struct mtd_info *mtd, loff_t ofs)
struct mtd_concat *concat = CONCAT(mtd);
int i, err = -EINVAL;
if (!concat->subdev[0]->block_markbad)
if (!mtd_can_have_bb(concat->subdev[0]))
return 0;
if (ofs > mtd->size)
return -EINVAL;
for (i = 0; i < concat->num_subdev; i++) {
struct mtd_info *subdev = concat->subdev[i];
......@@ -556,7 +522,7 @@ static int concat_block_markbad(struct mtd_info *mtd, loff_t ofs)
continue;
}
err = subdev->block_markbad(subdev, ofs);
err = mtd_block_markbad(subdev, ofs);
if (!err)
mtd->ecc_stats.badblocks++;
break;
......@@ -609,14 +575,14 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c
concat->mtd.subpage_sft = subdev[0]->subpage_sft;
concat->mtd.oobsize = subdev[0]->oobsize;
concat->mtd.oobavail = subdev[0]->oobavail;
if (subdev[0]->read_oob)
concat->mtd.read_oob = concat_read_oob;
if (subdev[0]->write_oob)
concat->mtd.write_oob = concat_write_oob;
if (subdev[0]->block_isbad)
concat->mtd.block_isbad = concat_block_isbad;
if (subdev[0]->block_markbad)
concat->mtd.block_markbad = concat_block_markbad;
if (subdev[0]->_read_oob)
concat->mtd._read_oob = concat_read_oob;
if (subdev[0]->_write_oob)
concat->mtd._write_oob = concat_write_oob;
if (subdev[0]->_block_isbad)
concat->mtd._block_isbad = concat_block_isbad;
if (subdev[0]->_block_markbad)
concat->mtd._block_markbad = concat_block_markbad;
concat->mtd.ecc_stats.badblocks = subdev[0]->ecc_stats.badblocks;
......@@ -653,8 +619,8 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c
if (concat->mtd.writesize != subdev[i]->writesize ||
concat->mtd.subpage_sft != subdev[i]->subpage_sft ||
concat->mtd.oobsize != subdev[i]->oobsize ||
!concat->mtd.read_oob != !subdev[i]->read_oob ||
!concat->mtd.write_oob != !subdev[i]->write_oob) {
!concat->mtd._read_oob != !subdev[i]->_read_oob ||
!concat->mtd._write_oob != !subdev[i]->_write_oob) {
kfree(concat);
printk("Incompatible OOB or ECC data on \"%s\"\n",
subdev[i]->name);
......@@ -669,12 +635,12 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c
concat->num_subdev = num_devs;
concat->mtd.name = name;
concat->mtd.erase = concat_erase;
concat->mtd.read = concat_read;
concat->mtd.write = concat_write;
concat->mtd.sync = concat_sync;
concat->mtd.lock = concat_lock;
concat->mtd.unlock = concat_unlock;
concat->mtd._erase = concat_erase;
concat->mtd._read = concat_read;
concat->mtd._write = concat_write;
concat->mtd._sync = concat_sync;
concat->mtd._lock = concat_lock;
concat->mtd._unlock = concat_unlock;
/*
* Combine the erase block size info of the subdevices:
......
......@@ -25,6 +25,11 @@ int add_mtd_device(struct mtd_info *mtd)
mtd->index = i;
mtd->usecount = 0;
/* default value if not set by driver */
if (mtd->bitflip_threshold == 0)
mtd->bitflip_threshold = mtd->ecc_strength;
/* No need to get a refcount on the module containing
the notifier, since we hold the mtd_table_mutex */
......@@ -186,3 +191,189 @@ void mtd_get_len_incl_bad(struct mtd_info *mtd, uint64_t offset,
}
}
#endif /* defined(CONFIG_CMD_MTDPARTS_SPREAD) */
/*
* Erase is an asynchronous operation. Device drivers are supposed
* to call instr->callback() whenever the operation completes, even
* if it completes with a failure.
* Callers are supposed to pass a callback function and wait for it
* to be called before writing to the block.
*/
int mtd_erase(struct mtd_info *mtd, struct erase_info *instr)
{
if (instr->addr > mtd->size || instr->len > mtd->size - instr->addr)
return -EINVAL;
if (!(mtd->flags & MTD_WRITEABLE))
return -EROFS;
instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN;
if (!instr->len) {
instr->state = MTD_ERASE_DONE;
mtd_erase_callback(instr);
return 0;
}
return mtd->_erase(mtd, instr);
}
int mtd_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen,
u_char *buf)
{
if (from < 0 || from > mtd->size || len > mtd->size - from)
return -EINVAL;
if (!len)
return 0;
return mtd->_read(mtd, from, len, retlen, buf);
}
int mtd_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen,
const u_char *buf)
{
*retlen = 0;
if (to < 0 || to > mtd->size || len > mtd->size - to)
return -EINVAL;
if (!mtd->_write || !(mtd->flags & MTD_WRITEABLE))
return -EROFS;
if (!len)
return 0;
return mtd->_write(mtd, to, len, retlen, buf);
}
/*
* In blackbox flight recorder like scenarios we want to make successful writes
* in interrupt context. panic_write() is only intended to be called when its
* known the kernel is about to panic and we need the write to succeed. Since
* the kernel is not going to be running for much longer, this function can
* break locks and delay to ensure the write succeeds (but not sleep).
*/
int mtd_panic_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen,
const u_char *buf)
{
*retlen = 0;
if (!mtd->_panic_write)
return -EOPNOTSUPP;
if (to < 0 || to > mtd->size || len > mtd->size - to)
return -EINVAL;
if (!(mtd->flags & MTD_WRITEABLE))
return -EROFS;
if (!len)
return 0;
return mtd->_panic_write(mtd, to, len, retlen, buf);
}
int mtd_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops)
{
ops->retlen = ops->oobretlen = 0;
if (!mtd->_read_oob)
return -EOPNOTSUPP;
return mtd->_read_oob(mtd, from, ops);
}
/*
* Method to access the protection register area, present in some flash
* devices. The user data is one time programmable but the factory data is read
* only.
*/
int mtd_get_fact_prot_info(struct mtd_info *mtd, struct otp_info *buf,
size_t len)
{
if (!mtd->_get_fact_prot_info)
return -EOPNOTSUPP;
if (!len)
return 0;
return mtd->_get_fact_prot_info(mtd, buf, len);
}
int mtd_read_fact_prot_reg(struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, u_char *buf)
{
*retlen = 0;
if (!mtd->_read_fact_prot_reg)
return -EOPNOTSUPP;
if (!len)
return 0;
return mtd->_read_fact_prot_reg(mtd, from, len, retlen, buf);
}
int mtd_get_user_prot_info(struct mtd_info *mtd, struct otp_info *buf,
size_t len)
{
if (!mtd->_get_user_prot_info)
return -EOPNOTSUPP;
if (!len)
return 0;
return mtd->_get_user_prot_info(mtd, buf, len);
}
int mtd_read_user_prot_reg(struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, u_char *buf)
{
*retlen = 0;
if (!mtd->_read_user_prot_reg)
return -EOPNOTSUPP;
if (!len)
return 0;
return mtd->_read_user_prot_reg(mtd, from, len, retlen, buf);
}
int mtd_write_user_prot_reg(struct mtd_info *mtd, loff_t to, size_t len,
size_t *retlen, u_char *buf)
{
*retlen = 0;
if (!mtd->_write_user_prot_reg)
return -EOPNOTSUPP;
if (!len)
return 0;
return mtd->_write_user_prot_reg(mtd, to, len, retlen, buf);
}
int mtd_lock_user_prot_reg(struct mtd_info *mtd, loff_t from, size_t len)
{
if (!mtd->_lock_user_prot_reg)
return -EOPNOTSUPP;
if (!len)
return 0;
return mtd->_lock_user_prot_reg(mtd, from, len);
}
/* Chip-supported device locking */
int mtd_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
{