Lines Matching +full:mtd +full:- +full:name

1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * MTD device concatenation layer
6 * Copyright © 2002-2010 David Woodhouse <dwmw2@infradead.org>
16 #include <linux/backing-dev.h>
18 #include <linux/mtd/mtd.h>
19 #include <linux/mtd/concat.h>
30 struct mtd_info mtd; member
43 * Given a pointer to the MTD object in the mtd_concat structure,
49 * MTD methods which look up the relevant subdevice, translate the
54 concat_read(struct mtd_info *mtd, loff_t from, size_t len, in concat_read() argument
57 struct mtd_concat *concat = CONCAT(mtd); in concat_read()
61 for (i = 0; i < concat->num_subdev; i++) { in concat_read()
62 struct mtd_info *subdev = concat->subdev[i]; in concat_read()
65 if (from >= subdev->size) { in concat_read()
68 from -= subdev->size; in concat_read()
71 if (from + len > subdev->size) in concat_read()
73 size = subdev->size - from; in concat_read()
83 mtd->ecc_stats.failed++; in concat_read()
86 mtd->ecc_stats.corrected++; in concat_read()
87 /* Do not overwrite -EBADMSG !! */ in concat_read()
95 len -= size; in concat_read()
102 return -EINVAL; in concat_read()
106 concat_panic_write(struct mtd_info *mtd, loff_t to, size_t len, in concat_panic_write() argument
109 struct mtd_concat *concat = CONCAT(mtd); in concat_panic_write()
110 int err = -EINVAL; in concat_panic_write()
112 for (i = 0; i < concat->num_subdev; i++) { in concat_panic_write()
113 struct mtd_info *subdev = concat->subdev[i]; in concat_panic_write()
116 if (to >= subdev->size) { in concat_panic_write()
117 to -= subdev->size; in concat_panic_write()
120 if (to + len > subdev->size) in concat_panic_write()
121 size = subdev->size - to; in concat_panic_write()
126 if (err == -EOPNOTSUPP) { in concat_panic_write()
134 len -= size; in concat_panic_write()
138 err = -EINVAL; in concat_panic_write()
147 concat_write(struct mtd_info *mtd, loff_t to, size_t len, in concat_write() argument
150 struct mtd_concat *concat = CONCAT(mtd); in concat_write()
151 int err = -EINVAL; in concat_write()
154 for (i = 0; i < concat->num_subdev; i++) { in concat_write()
155 struct mtd_info *subdev = concat->subdev[i]; in concat_write()
158 if (to >= subdev->size) { in concat_write()
160 to -= subdev->size; in concat_write()
163 if (to + len > subdev->size) in concat_write()
164 size = subdev->size - to; in concat_write()
173 len -= size; in concat_write()
177 err = -EINVAL; in concat_write()
185 concat_writev(struct mtd_info *mtd, const struct kvec *vecs, in concat_writev() argument
188 struct mtd_concat *concat = CONCAT(mtd); in concat_writev()
193 int err = -EINVAL; in concat_writev()
200 if (mtd->writesize > 1) { in concat_writev()
202 if (do_div(__to, mtd->writesize) || (total_len % mtd->writesize)) in concat_writev()
203 return -EINVAL; in concat_writev()
209 return -ENOMEM; in concat_writev()
212 for (i = 0; i < concat->num_subdev; i++) { in concat_writev()
213 struct mtd_info *subdev = concat->subdev[i]; in concat_writev()
216 if (to >= subdev->size) { in concat_writev()
217 to -= subdev->size; in concat_writev()
221 size = min_t(uint64_t, total_len, subdev->size - to); in concat_writev()
228 size -= vecs_copy[entry_high++].iov_len; in concat_writev()
235 entry_high - entry_low + 1, to, &retsize); in concat_writev()
237 vecs_copy[entry_high].iov_len = old_iov_len - size; in concat_writev()
246 total_len -= wsize; in concat_writev()
251 err = -EINVAL; in concat_writev()
260 concat_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) in concat_read_oob() argument
262 struct mtd_concat *concat = CONCAT(mtd); in concat_read_oob()
266 ops->retlen = ops->oobretlen = 0; in concat_read_oob()
268 for (i = 0; i < concat->num_subdev; i++) { in concat_read_oob()
269 struct mtd_info *subdev = concat->subdev[i]; in concat_read_oob()
271 if (from >= subdev->size) { in concat_read_oob()
272 from -= subdev->size; in concat_read_oob()
277 if (from + devops.len > subdev->size) in concat_read_oob()
278 devops.len = subdev->size - from; in concat_read_oob()
281 ops->retlen += devops.retlen; in concat_read_oob()
282 ops->oobretlen += devops.oobretlen; in concat_read_oob()
287 mtd->ecc_stats.failed++; in concat_read_oob()
290 mtd->ecc_stats.corrected++; in concat_read_oob()
291 /* Do not overwrite -EBADMSG !! */ in concat_read_oob()
299 devops.len = ops->len - ops->retlen; in concat_read_oob()
305 devops.ooblen = ops->ooblen - ops->oobretlen; in concat_read_oob()
308 devops.oobbuf += ops->oobretlen; in concat_read_oob()
313 return -EINVAL; in concat_read_oob()
317 concat_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) in concat_write_oob() argument
319 struct mtd_concat *concat = CONCAT(mtd); in concat_write_oob()
323 if (!(mtd->flags & MTD_WRITEABLE)) in concat_write_oob()
324 return -EROFS; in concat_write_oob()
326 ops->retlen = ops->oobretlen = 0; in concat_write_oob()
328 for (i = 0; i < concat->num_subdev; i++) { in concat_write_oob()
329 struct mtd_info *subdev = concat->subdev[i]; in concat_write_oob()
331 if (to >= subdev->size) { in concat_write_oob()
332 to -= subdev->size; in concat_write_oob()
337 if (to + devops.len > subdev->size) in concat_write_oob()
338 devops.len = subdev->size - to; in concat_write_oob()
341 ops->retlen += devops.retlen; in concat_write_oob()
342 ops->oobretlen += devops.oobretlen; in concat_write_oob()
347 devops.len = ops->len - ops->retlen; in concat_write_oob()
353 devops.ooblen = ops->ooblen - ops->oobretlen; in concat_write_oob()
360 return -EINVAL; in concat_write_oob()
363 static int concat_erase(struct mtd_info *mtd, struct erase_info *instr) in concat_erase() argument
365 struct mtd_concat *concat = CONCAT(mtd); in concat_erase()
372 * Check for proper erase block alignment of the to-be-erased area. in concat_erase()
374 * region info rather than looking at each particular sub-device in concat_erase()
377 if (!concat->mtd.numeraseregions) { in concat_erase()
379 if (instr->addr & (concat->mtd.erasesize - 1)) in concat_erase()
380 return -EINVAL; in concat_erase()
381 if (instr->len & (concat->mtd.erasesize - 1)) in concat_erase()
382 return -EINVAL; in concat_erase()
386 concat->mtd.eraseregions; in concat_erase()
389 * Find the erase region where the to-be-erased area begins: in concat_erase()
391 for (i = 0; i < concat->mtd.numeraseregions && in concat_erase()
392 instr->addr >= erase_regions[i].offset; i++) ; in concat_erase()
393 --i; in concat_erase()
397 * to-be-erased area begins. Verify that the starting in concat_erase()
400 if (i < 0 || instr->addr & (erase_regions[i].erasesize - 1)) in concat_erase()
401 return -EINVAL; in concat_erase()
404 * now find the erase region where the to-be-erased area ends: in concat_erase()
406 for (; i < concat->mtd.numeraseregions && in concat_erase()
407 (instr->addr + instr->len) >= erase_regions[i].offset; in concat_erase()
409 --i; in concat_erase()
413 if (i < 0 || ((instr->addr + instr->len) & in concat_erase()
414 (erase_regions[i].erasesize - 1))) in concat_erase()
415 return -EINVAL; in concat_erase()
422 return -ENOMEM; in concat_erase()
425 length = instr->len; in concat_erase()
428 * find the subdevice where the to-be-erased area begins, adjust in concat_erase()
431 for (i = 0; i < concat->num_subdev; i++) { in concat_erase()
432 subdev = concat->subdev[i]; in concat_erase()
433 if (subdev->size <= erase->addr) { in concat_erase()
434 erase->addr -= subdev->size; in concat_erase()
435 offset += subdev->size; in concat_erase()
442 BUG_ON(i >= concat->num_subdev); in concat_erase()
448 subdev = concat->subdev[i]; /* get current subdevice */ in concat_erase()
451 if (erase->addr + length > subdev->size) in concat_erase()
452 erase->len = subdev->size - erase->addr; in concat_erase()
454 erase->len = length; in concat_erase()
456 length -= erase->len; in concat_erase()
460 BUG_ON(err == -EINVAL); in concat_erase()
461 if (erase->fail_addr != MTD_FAIL_ADDR_UNKNOWN) in concat_erase()
462 instr->fail_addr = erase->fail_addr + offset; in concat_erase()
466 * erase->addr specifies the offset of the area to be in concat_erase()
468 * non-zero only the first time through this loop, i.e. in concat_erase()
473 erase->addr = 0; in concat_erase()
474 offset += subdev->size; in concat_erase()
481 static int concat_xxlock(struct mtd_info *mtd, loff_t ofs, uint64_t len, in concat_xxlock() argument
484 struct mtd_concat *concat = CONCAT(mtd); in concat_xxlock()
485 int i, err = -EINVAL; in concat_xxlock()
487 for (i = 0; i < concat->num_subdev; i++) { in concat_xxlock()
488 struct mtd_info *subdev = concat->subdev[i]; in concat_xxlock()
491 if (ofs >= subdev->size) { in concat_xxlock()
493 ofs -= subdev->size; in concat_xxlock()
496 if (ofs + len > subdev->size) in concat_xxlock()
497 size = subdev->size - ofs; in concat_xxlock()
508 len -= size; in concat_xxlock()
512 err = -EINVAL; in concat_xxlock()
519 static int concat_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) in concat_lock() argument
521 return concat_xxlock(mtd, ofs, len, true); in concat_lock()
524 static int concat_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) in concat_unlock() argument
526 return concat_xxlock(mtd, ofs, len, false); in concat_unlock()
529 static int concat_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len) in concat_is_locked() argument
531 struct mtd_concat *concat = CONCAT(mtd); in concat_is_locked()
532 int i, err = -EINVAL; in concat_is_locked()
534 for (i = 0; i < concat->num_subdev; i++) { in concat_is_locked()
535 struct mtd_info *subdev = concat->subdev[i]; in concat_is_locked()
537 if (ofs >= subdev->size) { in concat_is_locked()
538 ofs -= subdev->size; in concat_is_locked()
542 if (ofs + len > subdev->size) in concat_is_locked()
551 static void concat_sync(struct mtd_info *mtd) in concat_sync() argument
553 struct mtd_concat *concat = CONCAT(mtd); in concat_sync()
556 for (i = 0; i < concat->num_subdev; i++) { in concat_sync()
557 struct mtd_info *subdev = concat->subdev[i]; in concat_sync()
562 static int concat_suspend(struct mtd_info *mtd) in concat_suspend() argument
564 struct mtd_concat *concat = CONCAT(mtd); in concat_suspend()
567 for (i = 0; i < concat->num_subdev; i++) { in concat_suspend()
568 struct mtd_info *subdev = concat->subdev[i]; in concat_suspend()
575 static void concat_resume(struct mtd_info *mtd) in concat_resume() argument
577 struct mtd_concat *concat = CONCAT(mtd); in concat_resume()
580 for (i = 0; i < concat->num_subdev; i++) { in concat_resume()
581 struct mtd_info *subdev = concat->subdev[i]; in concat_resume()
586 static int concat_block_isbad(struct mtd_info *mtd, loff_t ofs) in concat_block_isbad() argument
588 struct mtd_concat *concat = CONCAT(mtd); in concat_block_isbad()
591 if (!mtd_can_have_bb(concat->subdev[0])) in concat_block_isbad()
594 for (i = 0; i < concat->num_subdev; i++) { in concat_block_isbad()
595 struct mtd_info *subdev = concat->subdev[i]; in concat_block_isbad()
597 if (ofs >= subdev->size) { in concat_block_isbad()
598 ofs -= subdev->size; in concat_block_isbad()
609 static int concat_block_markbad(struct mtd_info *mtd, loff_t ofs) in concat_block_markbad() argument
611 struct mtd_concat *concat = CONCAT(mtd); in concat_block_markbad()
612 int i, err = -EINVAL; in concat_block_markbad()
614 for (i = 0; i < concat->num_subdev; i++) { in concat_block_markbad()
615 struct mtd_info *subdev = concat->subdev[i]; in concat_block_markbad()
617 if (ofs >= subdev->size) { in concat_block_markbad()
618 ofs -= subdev->size; in concat_block_markbad()
624 mtd->ecc_stats.badblocks++; in concat_block_markbad()
632 * This function constructs a virtual MTD device by concatenating
633 * num_devs MTD devices. A pointer to the new device object is
639 const char *name) in mtd_concat_create() argument
640 { /* name for the new device */ in mtd_concat_create()
649 printk(KERN_NOTICE "Concatenating MTD devices:\n"); in mtd_concat_create()
651 printk(KERN_NOTICE "(%d): \"%s\"\n", i, subdev[i]->name); in mtd_concat_create()
652 printk(KERN_NOTICE "into device \"%s\"\n", name); in mtd_concat_create()
660 name); in mtd_concat_create()
663 concat->subdev = (struct mtd_info **) (concat + 1); in mtd_concat_create()
666 * Set up the new "super" device's MTD object structure, check for in mtd_concat_create()
669 concat->mtd.type = subdev[0]->type; in mtd_concat_create()
670 concat->mtd.flags = subdev[0]->flags; in mtd_concat_create()
671 concat->mtd.size = subdev[0]->size; in mtd_concat_create()
672 concat->mtd.erasesize = subdev[0]->erasesize; in mtd_concat_create()
673 concat->mtd.writesize = subdev[0]->writesize; in mtd_concat_create()
676 if (max_writebufsize < subdev[i]->writebufsize) in mtd_concat_create()
677 max_writebufsize = subdev[i]->writebufsize; in mtd_concat_create()
678 concat->mtd.writebufsize = max_writebufsize; in mtd_concat_create()
680 concat->mtd.subpage_sft = subdev[0]->subpage_sft; in mtd_concat_create()
681 concat->mtd.oobsize = subdev[0]->oobsize; in mtd_concat_create()
682 concat->mtd.oobavail = subdev[0]->oobavail; in mtd_concat_create()
685 if (subdev_master->_writev) in mtd_concat_create()
686 concat->mtd._writev = concat_writev; in mtd_concat_create()
687 if (subdev_master->_read_oob) in mtd_concat_create()
688 concat->mtd._read_oob = concat_read_oob; in mtd_concat_create()
689 if (subdev_master->_write_oob) in mtd_concat_create()
690 concat->mtd._write_oob = concat_write_oob; in mtd_concat_create()
691 if (subdev_master->_block_isbad) in mtd_concat_create()
692 concat->mtd._block_isbad = concat_block_isbad; in mtd_concat_create()
693 if (subdev_master->_block_markbad) in mtd_concat_create()
694 concat->mtd._block_markbad = concat_block_markbad; in mtd_concat_create()
695 if (subdev_master->_panic_write) in mtd_concat_create()
696 concat->mtd._panic_write = concat_panic_write; in mtd_concat_create()
697 if (subdev_master->_read) in mtd_concat_create()
698 concat->mtd._read = concat_read; in mtd_concat_create()
699 if (subdev_master->_write) in mtd_concat_create()
700 concat->mtd._write = concat_write; in mtd_concat_create()
702 concat->mtd.ecc_stats.badblocks = subdev[0]->ecc_stats.badblocks; in mtd_concat_create()
704 concat->subdev[0] = subdev[0]; in mtd_concat_create()
707 if (concat->mtd.type != subdev[i]->type) { in mtd_concat_create()
710 subdev[i]->name); in mtd_concat_create()
713 if (concat->mtd.flags != subdev[i]->flags) { in mtd_concat_create()
718 if ((concat->mtd.flags ^ subdev[i]-> in mtd_concat_create()
722 subdev[i]->name); in mtd_concat_create()
727 concat->mtd.flags |= in mtd_concat_create()
728 subdev[i]->flags & MTD_WRITEABLE; in mtd_concat_create()
732 concat->mtd.size += subdev[i]->size; in mtd_concat_create()
733 concat->mtd.ecc_stats.badblocks += in mtd_concat_create()
734 subdev[i]->ecc_stats.badblocks; in mtd_concat_create()
735 if (concat->mtd.writesize != subdev[i]->writesize || in mtd_concat_create()
736 concat->mtd.subpage_sft != subdev[i]->subpage_sft || in mtd_concat_create()
737 concat->mtd.oobsize != subdev[i]->oobsize || in mtd_concat_create()
738 !concat->mtd._read_oob != !subdev_master->_read_oob || in mtd_concat_create()
739 !concat->mtd._write_oob != !subdev_master->_write_oob) { in mtd_concat_create()
743 * mtd device. Check against subdev's master mtd in mtd_concat_create()
745 * subdev's callbacks is decided by master mtd device. in mtd_concat_create()
749 subdev[i]->name); in mtd_concat_create()
752 concat->subdev[i] = subdev[i]; in mtd_concat_create()
756 mtd_set_ooblayout(&concat->mtd, subdev[0]->ooblayout); in mtd_concat_create()
758 concat->num_subdev = num_devs; in mtd_concat_create()
759 concat->mtd.name = name; in mtd_concat_create()
761 concat->mtd._erase = concat_erase; in mtd_concat_create()
762 concat->mtd._sync = concat_sync; in mtd_concat_create()
763 concat->mtd._lock = concat_lock; in mtd_concat_create()
764 concat->mtd._unlock = concat_unlock; in mtd_concat_create()
765 concat->mtd._is_locked = concat_is_locked; in mtd_concat_create()
766 concat->mtd._suspend = concat_suspend; in mtd_concat_create()
767 concat->mtd._resume = concat_resume; in mtd_concat_create()
775 max_erasesize = curr_erasesize = subdev[0]->erasesize; in mtd_concat_create()
778 if (subdev[i]->numeraseregions == 0) { in mtd_concat_create()
780 if (subdev[i]->erasesize != curr_erasesize) { in mtd_concat_create()
783 curr_erasesize = subdev[i]->erasesize; in mtd_concat_create()
790 for (j = 0; j < subdev[i]->numeraseregions; j++) { in mtd_concat_create()
793 if (subdev[i]->eraseregions[j].erasesize != in mtd_concat_create()
797 subdev[i]->eraseregions[j]. in mtd_concat_create()
811 concat->mtd.erasesize = curr_erasesize; in mtd_concat_create()
812 concat->mtd.numeraseregions = 0; in mtd_concat_create()
823 concat->mtd.erasesize = max_erasesize; in mtd_concat_create()
824 concat->mtd.numeraseregions = num_erase_region; in mtd_concat_create()
825 concat->mtd.eraseregions = erase_region_p = in mtd_concat_create()
833 " for device \"%s\"\n", name); in mtd_concat_create()
841 curr_erasesize = subdev[0]->erasesize; in mtd_concat_create()
844 if (subdev[i]->numeraseregions == 0) { in mtd_concat_create()
846 if (subdev[i]->erasesize != curr_erasesize) { in mtd_concat_create()
851 erase_region_p->offset = begin; in mtd_concat_create()
852 erase_region_p->erasesize = in mtd_concat_create()
854 tmp64 = position - begin; in mtd_concat_create()
856 erase_region_p->numblocks = tmp64; in mtd_concat_create()
859 curr_erasesize = subdev[i]->erasesize; in mtd_concat_create()
862 position += subdev[i]->size; in mtd_concat_create()
866 for (j = 0; j < subdev[i]->numeraseregions; j++) { in mtd_concat_create()
868 if (subdev[i]->eraseregions[j]. in mtd_concat_create()
870 erase_region_p->offset = begin; in mtd_concat_create()
871 erase_region_p->erasesize = in mtd_concat_create()
873 tmp64 = position - begin; in mtd_concat_create()
875 erase_region_p->numblocks = tmp64; in mtd_concat_create()
879 subdev[i]->eraseregions[j]. in mtd_concat_create()
884 subdev[i]->eraseregions[j]. in mtd_concat_create()
890 erase_region_p->offset = begin; in mtd_concat_create()
891 erase_region_p->erasesize = curr_erasesize; in mtd_concat_create()
892 tmp64 = position - begin; in mtd_concat_create()
894 erase_region_p->numblocks = tmp64; in mtd_concat_create()
897 return &concat->mtd; in mtd_concat_create()
901 void mtd_concat_destroy(struct mtd_info *mtd) in mtd_concat_destroy() argument
903 struct mtd_concat *concat = CONCAT(mtd); in mtd_concat_destroy()
904 if (concat->mtd.numeraseregions) in mtd_concat_destroy()
905 kfree(concat->mtd.eraseregions); in mtd_concat_destroy()
914 MODULE_DESCRIPTION("Generic support for concatenating of MTD devices");