summaryrefslogtreecommitdiff
path: root/drivers/mtd/mtdconcat.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mtd/mtdconcat.c')
-rw-r--r--drivers/mtd/mtdconcat.c136
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);