.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
---|
1 | 2 | /* |
---|
2 | 3 | * MTD device concatenation layer |
---|
3 | 4 | * |
---|
.. | .. |
---|
5 | 6 | * Copyright © 2002-2010 David Woodhouse <dwmw2@infradead.org> |
---|
6 | 7 | * |
---|
7 | 8 | * 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 | | - * |
---|
23 | 9 | */ |
---|
24 | 10 | |
---|
25 | 11 | #include <linux/kernel.h> |
---|
.. | .. |
---|
115 | 101 | } |
---|
116 | 102 | return -EINVAL; |
---|
117 | 103 | } |
---|
| 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 | + |
---|
118 | 145 | |
---|
119 | 146 | static int |
---|
120 | 147 | concat_write(struct mtd_info *mtd, loff_t to, size_t len, |
---|
.. | .. |
---|
451 | 478 | return err; |
---|
452 | 479 | } |
---|
453 | 480 | |
---|
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) |
---|
455 | 483 | { |
---|
456 | 484 | struct mtd_concat *concat = CONCAT(mtd); |
---|
457 | 485 | int i, err = -EINVAL; |
---|
.. | .. |
---|
470 | 498 | else |
---|
471 | 499 | size = len; |
---|
472 | 500 | |
---|
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); |
---|
474 | 505 | if (err) |
---|
475 | 506 | break; |
---|
476 | 507 | |
---|
.. | .. |
---|
485 | 516 | return err; |
---|
486 | 517 | } |
---|
487 | 518 | |
---|
| 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 | + |
---|
488 | 524 | static int concat_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) |
---|
489 | 525 | { |
---|
| 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 | +{ |
---|
490 | 531 | struct mtd_concat *concat = CONCAT(mtd); |
---|
491 | | - int i, err = 0; |
---|
| 532 | + int i, err = -EINVAL; |
---|
492 | 533 | |
---|
493 | 534 | for (i = 0; i < concat->num_subdev; i++) { |
---|
494 | 535 | struct mtd_info *subdev = concat->subdev[i]; |
---|
495 | | - uint64_t size; |
---|
496 | 536 | |
---|
497 | 537 | if (ofs >= subdev->size) { |
---|
498 | | - size = 0; |
---|
499 | 538 | ofs -= subdev->size; |
---|
500 | 539 | continue; |
---|
501 | 540 | } |
---|
| 541 | + |
---|
502 | 542 | 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) |
---|
509 | 543 | break; |
---|
510 | 544 | |
---|
511 | | - len -= size; |
---|
512 | | - if (len == 0) |
---|
513 | | - break; |
---|
514 | | - |
---|
515 | | - err = -EINVAL; |
---|
516 | | - ofs = 0; |
---|
| 545 | + return mtd_is_locked(subdev, ofs, len); |
---|
517 | 546 | } |
---|
518 | 547 | |
---|
519 | 548 | return err; |
---|
.. | .. |
---|
612 | 641 | int i; |
---|
613 | 642 | size_t size; |
---|
614 | 643 | struct mtd_concat *concat; |
---|
| 644 | + struct mtd_info *subdev_master = NULL; |
---|
615 | 645 | uint32_t max_erasesize, curr_erasesize; |
---|
616 | 646 | int num_erase_region; |
---|
617 | 647 | int max_writebufsize = 0; |
---|
.. | .. |
---|
650 | 680 | concat->mtd.subpage_sft = subdev[0]->subpage_sft; |
---|
651 | 681 | concat->mtd.oobsize = subdev[0]->oobsize; |
---|
652 | 682 | 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) |
---|
654 | 686 | concat->mtd._writev = concat_writev; |
---|
655 | | - if (subdev[0]->_read_oob) |
---|
| 687 | + if (subdev_master->_read_oob) |
---|
656 | 688 | concat->mtd._read_oob = concat_read_oob; |
---|
657 | | - if (subdev[0]->_write_oob) |
---|
| 689 | + if (subdev_master->_write_oob) |
---|
658 | 690 | concat->mtd._write_oob = concat_write_oob; |
---|
659 | | - if (subdev[0]->_block_isbad) |
---|
| 691 | + if (subdev_master->_block_isbad) |
---|
660 | 692 | concat->mtd._block_isbad = concat_block_isbad; |
---|
661 | | - if (subdev[0]->_block_markbad) |
---|
| 693 | + if (subdev_master->_block_markbad) |
---|
662 | 694 | 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; |
---|
663 | 701 | |
---|
664 | 702 | concat->mtd.ecc_stats.badblocks = subdev[0]->ecc_stats.badblocks; |
---|
665 | 703 | |
---|
.. | .. |
---|
690 | 728 | subdev[i]->flags & MTD_WRITEABLE; |
---|
691 | 729 | } |
---|
692 | 730 | |
---|
| 731 | + subdev_master = mtd_get_master(subdev[i]); |
---|
693 | 732 | concat->mtd.size += subdev[i]->size; |
---|
694 | 733 | concat->mtd.ecc_stats.badblocks += |
---|
695 | 734 | subdev[i]->ecc_stats.badblocks; |
---|
696 | 735 | if (concat->mtd.writesize != subdev[i]->writesize || |
---|
697 | 736 | concat->mtd.subpage_sft != subdev[i]->subpage_sft || |
---|
698 | 737 | 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 | + */ |
---|
701 | 747 | kfree(concat); |
---|
702 | 748 | printk("Incompatible OOB or ECC data on \"%s\"\n", |
---|
703 | 749 | subdev[i]->name); |
---|
.. | .. |
---|
713 | 759 | concat->mtd.name = name; |
---|
714 | 760 | |
---|
715 | 761 | concat->mtd._erase = concat_erase; |
---|
716 | | - concat->mtd._read = concat_read; |
---|
717 | | - concat->mtd._write = concat_write; |
---|
718 | 762 | concat->mtd._sync = concat_sync; |
---|
719 | 763 | concat->mtd._lock = concat_lock; |
---|
720 | 764 | concat->mtd._unlock = concat_unlock; |
---|
| 765 | + concat->mtd._is_locked = concat_is_locked; |
---|
721 | 766 | concat->mtd._suspend = concat_suspend; |
---|
722 | 767 | concat->mtd._resume = concat_resume; |
---|
723 | 768 | |
---|
.. | .. |
---|
852 | 897 | return &concat->mtd; |
---|
853 | 898 | } |
---|
854 | 899 | |
---|
855 | | -/* |
---|
856 | | - * This function destroys an MTD object obtained from concat_mtd_devs() |
---|
857 | | - */ |
---|
858 | | - |
---|
| 900 | +/* Cleans the context obtained from mtd_concat_create() */ |
---|
859 | 901 | void mtd_concat_destroy(struct mtd_info *mtd) |
---|
860 | 902 | { |
---|
861 | 903 | struct mtd_concat *concat = CONCAT(mtd); |
---|