hc
2024-02-20 102a0743326a03cd1a1202ceda21e175b7d3575c
kernel/drivers/mtd/mtdconcat.c
....@@ -1,3 +1,4 @@
1
+// SPDX-License-Identifier: GPL-2.0-or-later
12 /*
23 * MTD device concatenation layer
34 *
....@@ -5,21 +6,6 @@
56 * Copyright © 2002-2010 David Woodhouse <dwmw2@infradead.org>
67 *
78 * NAND support by Christian Gan <cgan@iders.ca>
8
- *
9
- * This program is free software; you can redistribute it and/or modify
10
- * it under the terms of the GNU General Public License as published by
11
- * the Free Software Foundation; either version 2 of the License, or
12
- * (at your option) any later version.
13
- *
14
- * This program is distributed in the hope that it will be useful,
15
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
- * GNU General Public License for more details.
18
- *
19
- * You should have received a copy of the GNU General Public License
20
- * along with this program; if not, write to the Free Software
21
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22
- *
239 */
2410
2511 #include <linux/kernel.h>
....@@ -115,6 +101,47 @@
115101 }
116102 return -EINVAL;
117103 }
104
+
105
+static int
106
+concat_panic_write(struct mtd_info *mtd, loff_t to, size_t len,
107
+ size_t * retlen, const u_char * buf)
108
+{
109
+ struct mtd_concat *concat = CONCAT(mtd);
110
+ int err = -EINVAL;
111
+ int i;
112
+ for (i = 0; i < concat->num_subdev; i++) {
113
+ struct mtd_info *subdev = concat->subdev[i];
114
+ size_t size, retsize;
115
+
116
+ if (to >= subdev->size) {
117
+ to -= subdev->size;
118
+ continue;
119
+ }
120
+ if (to + len > subdev->size)
121
+ size = subdev->size - to;
122
+ else
123
+ size = len;
124
+
125
+ err = mtd_panic_write(subdev, to, size, &retsize, buf);
126
+ if (err == -EOPNOTSUPP) {
127
+ printk(KERN_ERR "mtdconcat: Cannot write from panic without panic_write\n");
128
+ return err;
129
+ }
130
+ if (err)
131
+ break;
132
+
133
+ *retlen += retsize;
134
+ len -= size;
135
+ if (len == 0)
136
+ break;
137
+
138
+ err = -EINVAL;
139
+ buf += size;
140
+ to = 0;
141
+ }
142
+ return err;
143
+}
144
+
118145
119146 static int
120147 concat_write(struct mtd_info *mtd, loff_t to, size_t len,
....@@ -451,7 +478,8 @@
451478 return err;
452479 }
453480
454
-static int concat_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
481
+static int concat_xxlock(struct mtd_info *mtd, loff_t ofs, uint64_t len,
482
+ bool is_lock)
455483 {
456484 struct mtd_concat *concat = CONCAT(mtd);
457485 int i, err = -EINVAL;
....@@ -470,7 +498,10 @@
470498 else
471499 size = len;
472500
473
- err = mtd_lock(subdev, ofs, size);
501
+ if (is_lock)
502
+ err = mtd_lock(subdev, ofs, size);
503
+ else
504
+ err = mtd_unlock(subdev, ofs, size);
474505 if (err)
475506 break;
476507
....@@ -485,35 +516,33 @@
485516 return err;
486517 }
487518
519
+static int concat_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
520
+{
521
+ return concat_xxlock(mtd, ofs, len, true);
522
+}
523
+
488524 static int concat_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
489525 {
526
+ return concat_xxlock(mtd, ofs, len, false);
527
+}
528
+
529
+static int concat_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len)
530
+{
490531 struct mtd_concat *concat = CONCAT(mtd);
491
- int i, err = 0;
532
+ int i, err = -EINVAL;
492533
493534 for (i = 0; i < concat->num_subdev; i++) {
494535 struct mtd_info *subdev = concat->subdev[i];
495
- uint64_t size;
496536
497537 if (ofs >= subdev->size) {
498
- size = 0;
499538 ofs -= subdev->size;
500539 continue;
501540 }
541
+
502542 if (ofs + len > subdev->size)
503
- size = subdev->size - ofs;
504
- else
505
- size = len;
506
-
507
- err = mtd_unlock(subdev, ofs, size);
508
- if (err)
509543 break;
510544
511
- len -= size;
512
- if (len == 0)
513
- break;
514
-
515
- err = -EINVAL;
516
- ofs = 0;
545
+ return mtd_is_locked(subdev, ofs, len);
517546 }
518547
519548 return err;
....@@ -612,6 +641,7 @@
612641 int i;
613642 size_t size;
614643 struct mtd_concat *concat;
644
+ struct mtd_info *subdev_master = NULL;
615645 uint32_t max_erasesize, curr_erasesize;
616646 int num_erase_region;
617647 int max_writebufsize = 0;
....@@ -650,16 +680,24 @@
650680 concat->mtd.subpage_sft = subdev[0]->subpage_sft;
651681 concat->mtd.oobsize = subdev[0]->oobsize;
652682 concat->mtd.oobavail = subdev[0]->oobavail;
653
- if (subdev[0]->_writev)
683
+
684
+ subdev_master = mtd_get_master(subdev[0]);
685
+ if (subdev_master->_writev)
654686 concat->mtd._writev = concat_writev;
655
- if (subdev[0]->_read_oob)
687
+ if (subdev_master->_read_oob)
656688 concat->mtd._read_oob = concat_read_oob;
657
- if (subdev[0]->_write_oob)
689
+ if (subdev_master->_write_oob)
658690 concat->mtd._write_oob = concat_write_oob;
659
- if (subdev[0]->_block_isbad)
691
+ if (subdev_master->_block_isbad)
660692 concat->mtd._block_isbad = concat_block_isbad;
661
- if (subdev[0]->_block_markbad)
693
+ if (subdev_master->_block_markbad)
662694 concat->mtd._block_markbad = concat_block_markbad;
695
+ if (subdev_master->_panic_write)
696
+ concat->mtd._panic_write = concat_panic_write;
697
+ if (subdev_master->_read)
698
+ concat->mtd._read = concat_read;
699
+ if (subdev_master->_write)
700
+ concat->mtd._write = concat_write;
663701
664702 concat->mtd.ecc_stats.badblocks = subdev[0]->ecc_stats.badblocks;
665703
....@@ -690,14 +728,22 @@
690728 subdev[i]->flags & MTD_WRITEABLE;
691729 }
692730
731
+ subdev_master = mtd_get_master(subdev[i]);
693732 concat->mtd.size += subdev[i]->size;
694733 concat->mtd.ecc_stats.badblocks +=
695734 subdev[i]->ecc_stats.badblocks;
696735 if (concat->mtd.writesize != subdev[i]->writesize ||
697736 concat->mtd.subpage_sft != subdev[i]->subpage_sft ||
698737 concat->mtd.oobsize != subdev[i]->oobsize ||
699
- !concat->mtd._read_oob != !subdev[i]->_read_oob ||
700
- !concat->mtd._write_oob != !subdev[i]->_write_oob) {
738
+ !concat->mtd._read_oob != !subdev_master->_read_oob ||
739
+ !concat->mtd._write_oob != !subdev_master->_write_oob) {
740
+ /*
741
+ * Check against subdev[i] for data members, because
742
+ * subdev's attributes may be different from master
743
+ * mtd device. Check against subdev's master mtd
744
+ * device for callbacks, because the existence of
745
+ * subdev's callbacks is decided by master mtd device.
746
+ */
701747 kfree(concat);
702748 printk("Incompatible OOB or ECC data on \"%s\"\n",
703749 subdev[i]->name);
....@@ -713,11 +759,10 @@
713759 concat->mtd.name = name;
714760
715761 concat->mtd._erase = concat_erase;
716
- concat->mtd._read = concat_read;
717
- concat->mtd._write = concat_write;
718762 concat->mtd._sync = concat_sync;
719763 concat->mtd._lock = concat_lock;
720764 concat->mtd._unlock = concat_unlock;
765
+ concat->mtd._is_locked = concat_is_locked;
721766 concat->mtd._suspend = concat_suspend;
722767 concat->mtd._resume = concat_resume;
723768
....@@ -852,10 +897,7 @@
852897 return &concat->mtd;
853898 }
854899
855
-/*
856
- * This function destroys an MTD object obtained from concat_mtd_devs()
857
- */
858
-
900
+/* Cleans the context obtained from mtd_concat_create() */
859901 void mtd_concat_destroy(struct mtd_info *mtd)
860902 {
861903 struct mtd_concat *concat = CONCAT(mtd);