diff options
Diffstat (limited to 'drivers/mtd/mtdconcat.c')
| -rw-r--r-- | drivers/mtd/mtdconcat.c | 136 |
1 files changed, 89 insertions, 47 deletions
diff --git a/drivers/mtd/mtdconcat.c b/drivers/mtd/mtdconcat.c index cbc5925e6440..f56f44aa8625 100644 --- a/drivers/mtd/mtdconcat.c +++ b/drivers/mtd/mtdconcat.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * MTD device concatenation layer * @@ -5,21 +6,6 @@ * Copyright © 2002-2010 David Woodhouse <dwmw2@infradead.org> * * NAND support by Christian Gan <cgan@iders.ca> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * */ #include <linux/kernel.h> @@ -117,6 +103,47 @@ concat_read(struct mtd_info *mtd, loff_t from, size_t len, } static int +concat_panic_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t * retlen, const u_char * buf) +{ + struct mtd_concat *concat = CONCAT(mtd); + int err = -EINVAL; + int i; + for (i = 0; i < concat->num_subdev; i++) { + struct mtd_info *subdev = concat->subdev[i]; + size_t size, retsize; + + if (to >= subdev->size) { + to -= subdev->size; + continue; + } + if (to + len > subdev->size) + size = subdev->size - to; + else + size = len; + + err = mtd_panic_write(subdev, to, size, &retsize, buf); + if (err == -EOPNOTSUPP) { + printk(KERN_ERR "mtdconcat: Cannot write from panic without panic_write\n"); + return err; + } + if (err) + break; + + *retlen += retsize; + len -= size; + if (len == 0) + break; + + err = -EINVAL; + buf += size; + to = 0; + } + return err; +} + + +static int concat_write(struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char * buf) { @@ -177,7 +204,7 @@ concat_writev(struct mtd_info *mtd, const struct kvec *vecs, } /* make a copy of vecs */ - vecs_copy = kmemdup(vecs, sizeof(struct kvec) * count, GFP_KERNEL); + vecs_copy = kmemdup_array(vecs, count, sizeof(struct kvec), GFP_KERNEL); if (!vecs_copy) return -ENOMEM; @@ -451,7 +478,8 @@ static int concat_erase(struct mtd_info *mtd, struct erase_info *instr) return err; } -static int concat_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) +static int concat_xxlock(struct mtd_info *mtd, loff_t ofs, uint64_t len, + bool is_lock) { struct mtd_concat *concat = CONCAT(mtd); int i, err = -EINVAL; @@ -470,7 +498,10 @@ static int concat_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) else size = len; - err = mtd_lock(subdev, ofs, size); + if (is_lock) + err = mtd_lock(subdev, ofs, size); + else + err = mtd_unlock(subdev, ofs, size); if (err) break; @@ -485,35 +516,33 @@ static int concat_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) return err; } +static int concat_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) +{ + return concat_xxlock(mtd, ofs, len, true); +} + static int concat_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) { + return concat_xxlock(mtd, ofs, len, false); +} + +static int concat_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len) +{ struct mtd_concat *concat = CONCAT(mtd); - int i, err = 0; + int i, err = -EINVAL; for (i = 0; i < concat->num_subdev; i++) { struct mtd_info *subdev = concat->subdev[i]; - uint64_t size; if (ofs >= subdev->size) { - size = 0; ofs -= subdev->size; continue; } - if (ofs + len > subdev->size) - size = subdev->size - ofs; - else - size = len; - err = mtd_unlock(subdev, ofs, size); - if (err) - break; - - len -= size; - if (len == 0) + if (ofs + len > subdev->size) break; - err = -EINVAL; - ofs = 0; + return mtd_is_locked(subdev, ofs, len); } return err; @@ -612,6 +641,7 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c int i; size_t size; struct mtd_concat *concat; + struct mtd_info *subdev_master = NULL; uint32_t max_erasesize, curr_erasesize; int num_erase_region; int max_writebufsize = 0; @@ -650,16 +680,24 @@ 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]->_writev) + + subdev_master = mtd_get_master(subdev[0]); + if (subdev_master->_writev) concat->mtd._writev = concat_writev; - if (subdev[0]->_read_oob) + if (subdev_master->_read_oob) concat->mtd._read_oob = concat_read_oob; - if (subdev[0]->_write_oob) + if (subdev_master->_write_oob) concat->mtd._write_oob = concat_write_oob; - if (subdev[0]->_block_isbad) + if (subdev_master->_block_isbad) concat->mtd._block_isbad = concat_block_isbad; - if (subdev[0]->_block_markbad) + if (subdev_master->_block_markbad) concat->mtd._block_markbad = concat_block_markbad; + if (subdev_master->_panic_write) + concat->mtd._panic_write = concat_panic_write; + if (subdev_master->_read) + concat->mtd._read = concat_read; + if (subdev_master->_write) + concat->mtd._write = concat_write; concat->mtd.ecc_stats.badblocks = subdev[0]->ecc_stats.badblocks; @@ -690,14 +728,22 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c subdev[i]->flags & MTD_WRITEABLE; } + subdev_master = mtd_get_master(subdev[i]); concat->mtd.size += subdev[i]->size; concat->mtd.ecc_stats.badblocks += subdev[i]->ecc_stats.badblocks; 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_master->_read_oob || + !concat->mtd._write_oob != !subdev_master->_write_oob) { + /* + * Check against subdev[i] for data members, because + * subdev's attributes may be different from master + * mtd device. Check against subdev's master mtd + * device for callbacks, because the existence of + * subdev's callbacks is decided by master mtd device. + */ kfree(concat); printk("Incompatible OOB or ECC data on \"%s\"\n", subdev[i]->name); @@ -713,11 +759,10 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c 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._is_locked = concat_is_locked; concat->mtd._suspend = concat_suspend; concat->mtd._resume = concat_resume; @@ -791,7 +836,7 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c /* * walk the map of the new device once more and fill in - * in erase region info: + * erase region info: */ curr_erasesize = subdev[0]->erasesize; begin = position = 0; @@ -852,10 +897,7 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c return &concat->mtd; } -/* - * This function destroys an MTD object obtained from concat_mtd_devs() - */ - +/* Cleans the context obtained from mtd_concat_create() */ void mtd_concat_destroy(struct mtd_info *mtd) { struct mtd_concat *concat = CONCAT(mtd); |
