xref: /linux/drivers/mtd/mtdconcat.c (revision e19eaffc5213fdd6179e849d3032929fae0d8c2c)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * MTD device concatenation layer
4  *
5  * Copyright © 2002 Robert Kaiser <rkaiser@sysgo.de>
6  * Copyright © 2002-2010 David Woodhouse <dwmw2@infradead.org>
7  *
8  * NAND support by Christian Gan <cgan@iders.ca>
9  */
10 
11 #include <linux/kernel.h>
12 #include <linux/module.h>
13 #include <linux/slab.h>
14 #include <linux/sched.h>
15 #include <linux/types.h>
16 #include <linux/backing-dev.h>
17 
18 #include <linux/mtd/mtd.h>
19 #include <linux/mtd/concat.h>
20 
21 #include <asm/div64.h>
22 
23 /*
24  * how to calculate the size required for the above structure,
25  * including the pointer array subdev points to:
26  */
27 #define SIZEOF_STRUCT_MTD_CONCAT(num_subdev)	\
28 	((sizeof(struct mtd_concat) + (num_subdev) * sizeof(struct mtd_info *)))
29 
30 /*
31  * Given a pointer to the MTD object in the mtd_concat structure,
32  * we can retrieve the pointer to that structure with this macro.
33  */
34 #define CONCAT(x)  ((struct mtd_concat *)(x))
35 
36 /*
37  * MTD methods which look up the relevant subdevice, translate the
38  * effective address and pass through to the subdevice.
39  */
40 
41 static int
42 concat_read(struct mtd_info *mtd, loff_t from, size_t len,
43 	    size_t * retlen, u_char * buf)
44 {
45 	struct mtd_concat *concat = CONCAT(mtd);
46 	int ret = 0, err;
47 	int i;
48 
49 	for (i = 0; i < concat->num_subdev; i++) {
50 		struct mtd_info *subdev = concat->subdev[i];
51 		size_t size, retsize;
52 
53 		if (from >= subdev->size) {
54 			/* Not destined for this subdev */
55 			size = 0;
56 			from -= subdev->size;
57 			continue;
58 		}
59 		if (from + len > subdev->size)
60 			/* First part goes into this subdev */
61 			size = subdev->size - from;
62 		else
63 			/* Entire transaction goes into this subdev */
64 			size = len;
65 
66 		err = mtd_read(subdev, from, size, &retsize, buf);
67 
68 		/* Save information about bitflips! */
69 		if (unlikely(err)) {
70 			if (mtd_is_eccerr(err)) {
71 				mtd->ecc_stats.failed++;
72 				ret = err;
73 			} else if (mtd_is_bitflip(err)) {
74 				mtd->ecc_stats.corrected++;
75 				/* Do not overwrite -EBADMSG !! */
76 				if (!ret)
77 					ret = err;
78 			} else
79 				return err;
80 		}
81 
82 		*retlen += retsize;
83 		len -= size;
84 		if (len == 0)
85 			return ret;
86 
87 		buf += size;
88 		from = 0;
89 	}
90 	return -EINVAL;
91 }
92 
93 static int
94 concat_panic_write(struct mtd_info *mtd, loff_t to, size_t len,
95 	     size_t * retlen, const u_char * buf)
96 {
97 	struct mtd_concat *concat = CONCAT(mtd);
98 	int err = -EINVAL;
99 	int i;
100 	for (i = 0; i < concat->num_subdev; i++) {
101 		struct mtd_info *subdev = concat->subdev[i];
102 		size_t size, retsize;
103 
104 		if (to >= subdev->size) {
105 			to -= subdev->size;
106 			continue;
107 		}
108 		if (to + len > subdev->size)
109 			size = subdev->size - to;
110 		else
111 			size = len;
112 
113 		err = mtd_panic_write(subdev, to, size, &retsize, buf);
114 		if (err == -EOPNOTSUPP) {
115 			printk(KERN_ERR "mtdconcat: Cannot write from panic without panic_write\n");
116 			return err;
117 		}
118 		if (err)
119 			break;
120 
121 		*retlen += retsize;
122 		len -= size;
123 		if (len == 0)
124 			break;
125 
126 		err = -EINVAL;
127 		buf += size;
128 		to = 0;
129 	}
130 	return err;
131 }
132 
133 
134 static int
135 concat_write(struct mtd_info *mtd, loff_t to, size_t len,
136 	     size_t * retlen, const u_char * buf)
137 {
138 	struct mtd_concat *concat = CONCAT(mtd);
139 	int err = -EINVAL;
140 	int i;
141 
142 	for (i = 0; i < concat->num_subdev; i++) {
143 		struct mtd_info *subdev = concat->subdev[i];
144 		size_t size, retsize;
145 
146 		if (to >= subdev->size) {
147 			size = 0;
148 			to -= subdev->size;
149 			continue;
150 		}
151 		if (to + len > subdev->size)
152 			size = subdev->size - to;
153 		else
154 			size = len;
155 
156 		err = mtd_write(subdev, to, size, &retsize, buf);
157 		if (err)
158 			break;
159 
160 		*retlen += retsize;
161 		len -= size;
162 		if (len == 0)
163 			break;
164 
165 		err = -EINVAL;
166 		buf += size;
167 		to = 0;
168 	}
169 	return err;
170 }
171 
172 static int
173 concat_writev(struct mtd_info *mtd, const struct kvec *vecs,
174 		unsigned long count, loff_t to, size_t * retlen)
175 {
176 	struct mtd_concat *concat = CONCAT(mtd);
177 	struct kvec *vecs_copy;
178 	unsigned long entry_low, entry_high;
179 	size_t total_len = 0;
180 	int i;
181 	int err = -EINVAL;
182 
183 	/* Calculate total length of data */
184 	for (i = 0; i < count; i++)
185 		total_len += vecs[i].iov_len;
186 
187 	/* Check alignment */
188 	if (mtd->writesize > 1) {
189 		uint64_t __to = to;
190 		if (do_div(__to, mtd->writesize) || (total_len % mtd->writesize))
191 			return -EINVAL;
192 	}
193 
194 	/* make a copy of vecs */
195 	vecs_copy = kmemdup_array(vecs, count, sizeof(struct kvec), GFP_KERNEL);
196 	if (!vecs_copy)
197 		return -ENOMEM;
198 
199 	entry_low = 0;
200 	for (i = 0; i < concat->num_subdev; i++) {
201 		struct mtd_info *subdev = concat->subdev[i];
202 		size_t size, wsize, retsize, old_iov_len;
203 
204 		if (to >= subdev->size) {
205 			to -= subdev->size;
206 			continue;
207 		}
208 
209 		size = min_t(uint64_t, total_len, subdev->size - to);
210 		wsize = size; /* store for future use */
211 
212 		entry_high = entry_low;
213 		while (entry_high < count) {
214 			if (size <= vecs_copy[entry_high].iov_len)
215 				break;
216 			size -= vecs_copy[entry_high++].iov_len;
217 		}
218 
219 		old_iov_len = vecs_copy[entry_high].iov_len;
220 		vecs_copy[entry_high].iov_len = size;
221 
222 		err = mtd_writev(subdev, &vecs_copy[entry_low],
223 				 entry_high - entry_low + 1, to, &retsize);
224 
225 		vecs_copy[entry_high].iov_len = old_iov_len - size;
226 		vecs_copy[entry_high].iov_base += size;
227 
228 		entry_low = entry_high;
229 
230 		if (err)
231 			break;
232 
233 		*retlen += retsize;
234 		total_len -= wsize;
235 
236 		if (total_len == 0)
237 			break;
238 
239 		err = -EINVAL;
240 		to = 0;
241 	}
242 
243 	kfree(vecs_copy);
244 	return err;
245 }
246 
247 static int
248 concat_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops)
249 {
250 	struct mtd_concat *concat = CONCAT(mtd);
251 	struct mtd_oob_ops devops = *ops;
252 	int i, err, ret = 0;
253 
254 	ops->retlen = ops->oobretlen = 0;
255 
256 	for (i = 0; i < concat->num_subdev; i++) {
257 		struct mtd_info *subdev = concat->subdev[i];
258 
259 		if (from >= subdev->size) {
260 			from -= subdev->size;
261 			continue;
262 		}
263 
264 		/* partial read ? */
265 		if (from + devops.len > subdev->size)
266 			devops.len = subdev->size - from;
267 
268 		err = mtd_read_oob(subdev, from, &devops);
269 		ops->retlen += devops.retlen;
270 		ops->oobretlen += devops.oobretlen;
271 
272 		/* Save information about bitflips! */
273 		if (unlikely(err)) {
274 			if (mtd_is_eccerr(err)) {
275 				mtd->ecc_stats.failed++;
276 				ret = err;
277 			} else if (mtd_is_bitflip(err)) {
278 				mtd->ecc_stats.corrected++;
279 				/* Do not overwrite -EBADMSG !! */
280 				if (!ret)
281 					ret = err;
282 			} else
283 				return err;
284 		}
285 
286 		if (devops.datbuf) {
287 			devops.len = ops->len - ops->retlen;
288 			if (!devops.len)
289 				return ret;
290 			devops.datbuf += devops.retlen;
291 		}
292 		if (devops.oobbuf) {
293 			devops.ooblen = ops->ooblen - ops->oobretlen;
294 			if (!devops.ooblen)
295 				return ret;
296 			devops.oobbuf += ops->oobretlen;
297 		}
298 
299 		from = 0;
300 	}
301 	return -EINVAL;
302 }
303 
304 static int
305 concat_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops)
306 {
307 	struct mtd_concat *concat = CONCAT(mtd);
308 	struct mtd_oob_ops devops = *ops;
309 	int i, err;
310 
311 	if (!(mtd->flags & MTD_WRITEABLE))
312 		return -EROFS;
313 
314 	ops->retlen = ops->oobretlen = 0;
315 
316 	for (i = 0; i < concat->num_subdev; i++) {
317 		struct mtd_info *subdev = concat->subdev[i];
318 
319 		if (to >= subdev->size) {
320 			to -= subdev->size;
321 			continue;
322 		}
323 
324 		/* partial write ? */
325 		if (to + devops.len > subdev->size)
326 			devops.len = subdev->size - to;
327 
328 		err = mtd_write_oob(subdev, to, &devops);
329 		ops->retlen += devops.retlen;
330 		ops->oobretlen += devops.oobretlen;
331 		if (err)
332 			return err;
333 
334 		if (devops.datbuf) {
335 			devops.len = ops->len - ops->retlen;
336 			if (!devops.len)
337 				return 0;
338 			devops.datbuf += devops.retlen;
339 		}
340 		if (devops.oobbuf) {
341 			devops.ooblen = ops->ooblen - ops->oobretlen;
342 			if (!devops.ooblen)
343 				return 0;
344 			devops.oobbuf += devops.oobretlen;
345 		}
346 		to = 0;
347 	}
348 	return -EINVAL;
349 }
350 
351 static int concat_erase(struct mtd_info *mtd, struct erase_info *instr)
352 {
353 	struct mtd_concat *concat = CONCAT(mtd);
354 	struct mtd_info *subdev;
355 	int i, err;
356 	uint64_t length, offset = 0;
357 	struct erase_info *erase;
358 
359 	/*
360 	 * Check for proper erase block alignment of the to-be-erased area.
361 	 * It is easier to do this based on the super device's erase
362 	 * region info rather than looking at each particular sub-device
363 	 * in turn.
364 	 */
365 	if (!concat->mtd.numeraseregions) {
366 		/* the easy case: device has uniform erase block size */
367 		if (instr->addr & (concat->mtd.erasesize - 1))
368 			return -EINVAL;
369 		if (instr->len & (concat->mtd.erasesize - 1))
370 			return -EINVAL;
371 	} else {
372 		/* device has variable erase size */
373 		struct mtd_erase_region_info *erase_regions =
374 		    concat->mtd.eraseregions;
375 
376 		/*
377 		 * Find the erase region where the to-be-erased area begins:
378 		 */
379 		for (i = 0; i < concat->mtd.numeraseregions &&
380 		     instr->addr >= erase_regions[i].offset; i++) ;
381 		--i;
382 
383 		/*
384 		 * Now erase_regions[i] is the region in which the
385 		 * to-be-erased area begins. Verify that the starting
386 		 * offset is aligned to this region's erase size:
387 		 */
388 		if (i < 0 || instr->addr & (erase_regions[i].erasesize - 1))
389 			return -EINVAL;
390 
391 		/*
392 		 * now find the erase region where the to-be-erased area ends:
393 		 */
394 		for (; i < concat->mtd.numeraseregions &&
395 		     (instr->addr + instr->len) >= erase_regions[i].offset;
396 		     ++i) ;
397 		--i;
398 		/*
399 		 * check if the ending offset is aligned to this region's erase size
400 		 */
401 		if (i < 0 || ((instr->addr + instr->len) &
402 					(erase_regions[i].erasesize - 1)))
403 			return -EINVAL;
404 	}
405 
406 	/* make a local copy of instr to avoid modifying the caller's struct */
407 	erase = kmalloc_obj(struct erase_info);
408 
409 	if (!erase)
410 		return -ENOMEM;
411 
412 	*erase = *instr;
413 	length = instr->len;
414 
415 	/*
416 	 * find the subdevice where the to-be-erased area begins, adjust
417 	 * starting offset to be relative to the subdevice start
418 	 */
419 	for (i = 0; i < concat->num_subdev; i++) {
420 		subdev = concat->subdev[i];
421 		if (subdev->size <= erase->addr) {
422 			erase->addr -= subdev->size;
423 			offset += subdev->size;
424 		} else {
425 			break;
426 		}
427 	}
428 
429 	/* must never happen since size limit has been verified above */
430 	BUG_ON(i >= concat->num_subdev);
431 
432 	/* now do the erase: */
433 	err = 0;
434 	for (; length > 0; i++) {
435 		/* loop for all subdevices affected by this request */
436 		subdev = concat->subdev[i];	/* get current subdevice */
437 
438 		/* limit length to subdevice's size: */
439 		if (erase->addr + length > subdev->size)
440 			erase->len = subdev->size - erase->addr;
441 		else
442 			erase->len = length;
443 
444 		length -= erase->len;
445 		if ((err = mtd_erase(subdev, erase))) {
446 			/* sanity check: should never happen since
447 			 * block alignment has been checked above */
448 			BUG_ON(err == -EINVAL);
449 			if (erase->fail_addr != MTD_FAIL_ADDR_UNKNOWN)
450 				instr->fail_addr = erase->fail_addr + offset;
451 			break;
452 		}
453 		/*
454 		 * erase->addr specifies the offset of the area to be
455 		 * erased *within the current subdevice*. It can be
456 		 * non-zero only the first time through this loop, i.e.
457 		 * for the first subdevice where blocks need to be erased.
458 		 * All the following erases must begin at the start of the
459 		 * current subdevice, i.e. at offset zero.
460 		 */
461 		erase->addr = 0;
462 		offset += subdev->size;
463 	}
464 	kfree(erase);
465 
466 	return err;
467 }
468 
469 static int concat_xxlock(struct mtd_info *mtd, loff_t ofs, uint64_t len,
470 			 bool is_lock)
471 {
472 	struct mtd_concat *concat = CONCAT(mtd);
473 	int i, err = -EINVAL;
474 
475 	for (i = 0; i < concat->num_subdev; i++) {
476 		struct mtd_info *subdev = concat->subdev[i];
477 		uint64_t size;
478 
479 		if (ofs >= subdev->size) {
480 			size = 0;
481 			ofs -= subdev->size;
482 			continue;
483 		}
484 		if (ofs + len > subdev->size)
485 			size = subdev->size - ofs;
486 		else
487 			size = len;
488 
489 		if (is_lock)
490 			err = mtd_lock(subdev, ofs, size);
491 		else
492 			err = mtd_unlock(subdev, ofs, size);
493 		if (err)
494 			break;
495 
496 		len -= size;
497 		if (len == 0)
498 			break;
499 
500 		err = -EINVAL;
501 		ofs = 0;
502 	}
503 
504 	return err;
505 }
506 
507 static int concat_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
508 {
509 	return concat_xxlock(mtd, ofs, len, true);
510 }
511 
512 static int concat_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
513 {
514 	return concat_xxlock(mtd, ofs, len, false);
515 }
516 
517 static int concat_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len)
518 {
519 	struct mtd_concat *concat = CONCAT(mtd);
520 	int i, err = -EINVAL;
521 
522 	for (i = 0; i < concat->num_subdev; i++) {
523 		struct mtd_info *subdev = concat->subdev[i];
524 
525 		if (ofs >= subdev->size) {
526 			ofs -= subdev->size;
527 			continue;
528 		}
529 
530 		if (ofs + len > subdev->size)
531 			break;
532 
533 		return mtd_is_locked(subdev, ofs, len);
534 	}
535 
536 	return err;
537 }
538 
539 static void concat_sync(struct mtd_info *mtd)
540 {
541 	struct mtd_concat *concat = CONCAT(mtd);
542 	int i;
543 
544 	for (i = 0; i < concat->num_subdev; i++) {
545 		struct mtd_info *subdev = concat->subdev[i];
546 		mtd_sync(subdev);
547 	}
548 }
549 
550 static int concat_suspend(struct mtd_info *mtd)
551 {
552 	struct mtd_concat *concat = CONCAT(mtd);
553 	int i, rc = 0;
554 
555 	for (i = 0; i < concat->num_subdev; i++) {
556 		struct mtd_info *subdev = concat->subdev[i];
557 		if ((rc = mtd_suspend(subdev)) < 0)
558 			return rc;
559 	}
560 	return rc;
561 }
562 
563 static void concat_resume(struct mtd_info *mtd)
564 {
565 	struct mtd_concat *concat = CONCAT(mtd);
566 	int i;
567 
568 	for (i = 0; i < concat->num_subdev; i++) {
569 		struct mtd_info *subdev = concat->subdev[i];
570 		mtd_resume(subdev);
571 	}
572 }
573 
574 static int concat_block_isbad(struct mtd_info *mtd, loff_t ofs)
575 {
576 	struct mtd_concat *concat = CONCAT(mtd);
577 	int i, res = 0;
578 
579 	if (!mtd_can_have_bb(concat->subdev[0]))
580 		return res;
581 
582 	for (i = 0; i < concat->num_subdev; i++) {
583 		struct mtd_info *subdev = concat->subdev[i];
584 
585 		if (ofs >= subdev->size) {
586 			ofs -= subdev->size;
587 			continue;
588 		}
589 
590 		res = mtd_block_isbad(subdev, ofs);
591 		break;
592 	}
593 
594 	return res;
595 }
596 
597 static int concat_block_markbad(struct mtd_info *mtd, loff_t ofs)
598 {
599 	struct mtd_concat *concat = CONCAT(mtd);
600 	int i, err = -EINVAL;
601 
602 	for (i = 0; i < concat->num_subdev; i++) {
603 		struct mtd_info *subdev = concat->subdev[i];
604 
605 		if (ofs >= subdev->size) {
606 			ofs -= subdev->size;
607 			continue;
608 		}
609 
610 		err = mtd_block_markbad(subdev, ofs);
611 		if (!err)
612 			mtd->ecc_stats.badblocks++;
613 		break;
614 	}
615 
616 	return err;
617 }
618 
619 /*
620  * This function constructs a virtual MTD device by concatenating
621  * num_devs MTD devices. A pointer to the new device object is
622  * stored to *new_dev upon success. This function does _not_
623  * register any devices: this is the caller's responsibility.
624  */
625 struct mtd_info *mtd_concat_create(struct mtd_info *subdev[],	/* subdevices to concatenate */
626 				   int num_devs,	/* number of subdevices      */
627 				   const char *name)
628 {				/* name for the new device   */
629 	int i;
630 	struct mtd_concat *concat;
631 	struct mtd_info *subdev_master = NULL;
632 	uint32_t max_erasesize, curr_erasesize;
633 	int num_erase_region;
634 	int max_writebufsize = 0;
635 
636 	printk(KERN_NOTICE "Concatenating MTD devices:\n");
637 	for (i = 0; i < num_devs; i++)
638 		printk(KERN_NOTICE "(%d): \"%s\"\n", i, subdev[i]->name);
639 	printk(KERN_NOTICE "into device \"%s\"\n", name);
640 
641 	/* allocate the device structure */
642 	concat = kzalloc_flex(*concat, subdev, num_devs, GFP_KERNEL);
643 	if (!concat) {
644 		printk
645 		    ("memory allocation error while creating concatenated device \"%s\"\n",
646 		     name);
647 		return NULL;
648 	}
649 
650 	/*
651 	 * Set up the new "super" device's MTD object structure, check for
652 	 * incompatibilities between the subdevices.
653 	 */
654 	concat->mtd.type = subdev[0]->type;
655 	concat->mtd.flags = subdev[0]->flags;
656 	concat->mtd.size = subdev[0]->size;
657 	concat->mtd.erasesize = subdev[0]->erasesize;
658 	concat->mtd.writesize = subdev[0]->writesize;
659 
660 	for (i = 0; i < num_devs; i++)
661 		if (max_writebufsize < subdev[i]->writebufsize)
662 			max_writebufsize = subdev[i]->writebufsize;
663 	concat->mtd.writebufsize = max_writebufsize;
664 
665 	concat->mtd.subpage_sft = subdev[0]->subpage_sft;
666 	concat->mtd.oobsize = subdev[0]->oobsize;
667 	concat->mtd.oobavail = subdev[0]->oobavail;
668 
669 	subdev_master = mtd_get_master(subdev[0]);
670 	if (subdev_master->_writev)
671 		concat->mtd._writev = concat_writev;
672 	if (subdev_master->_read_oob)
673 		concat->mtd._read_oob = concat_read_oob;
674 	if (subdev_master->_write_oob)
675 		concat->mtd._write_oob = concat_write_oob;
676 	if (subdev_master->_block_isbad)
677 		concat->mtd._block_isbad = concat_block_isbad;
678 	if (subdev_master->_block_markbad)
679 		concat->mtd._block_markbad = concat_block_markbad;
680 	if (subdev_master->_panic_write)
681 		concat->mtd._panic_write = concat_panic_write;
682 	if (subdev_master->_read)
683 		concat->mtd._read = concat_read;
684 	if (subdev_master->_write)
685 		concat->mtd._write = concat_write;
686 
687 	concat->mtd.ecc_stats.badblocks = subdev[0]->ecc_stats.badblocks;
688 
689 	concat->subdev[0] = subdev[0];
690 
691 	for (i = 1; i < num_devs; i++) {
692 		if (concat->mtd.type != subdev[i]->type) {
693 			kfree(concat);
694 			printk("Incompatible device type on \"%s\"\n",
695 			       subdev[i]->name);
696 			return NULL;
697 		}
698 		if (concat->mtd.flags != subdev[i]->flags) {
699 			/*
700 			 * Expect all flags except MTD_WRITEABLE to be
701 			 * equal on all subdevices.
702 			 */
703 			if ((concat->mtd.flags ^ subdev[i]->
704 			     flags) & ~MTD_WRITEABLE) {
705 				kfree(concat);
706 				printk("Incompatible device flags on \"%s\"\n",
707 				       subdev[i]->name);
708 				return NULL;
709 			} else
710 				/* if writeable attribute differs,
711 				   make super device writeable */
712 				concat->mtd.flags |=
713 				    subdev[i]->flags & MTD_WRITEABLE;
714 		}
715 
716 		subdev_master = mtd_get_master(subdev[i]);
717 		concat->mtd.size += subdev[i]->size;
718 		concat->mtd.ecc_stats.badblocks +=
719 			subdev[i]->ecc_stats.badblocks;
720 		if (concat->mtd.writesize   !=  subdev[i]->writesize ||
721 		    concat->mtd.subpage_sft != subdev[i]->subpage_sft ||
722 		    concat->mtd.oobsize    !=  subdev[i]->oobsize ||
723 		    !concat->mtd._read_oob  != !subdev_master->_read_oob ||
724 		    !concat->mtd._write_oob != !subdev_master->_write_oob) {
725 			/*
726 			 * Check against subdev[i] for data members, because
727 			 * subdev's attributes may be different from master
728 			 * mtd device. Check against subdev's master mtd
729 			 * device for callbacks, because the existence of
730 			 * subdev's callbacks is decided by master mtd device.
731 			 */
732 			kfree(concat);
733 			printk("Incompatible OOB or ECC data on \"%s\"\n",
734 			       subdev[i]->name);
735 			return NULL;
736 		}
737 		concat->subdev[i] = subdev[i];
738 
739 	}
740 
741 	mtd_set_ooblayout(&concat->mtd, subdev[0]->ooblayout);
742 
743 	concat->num_subdev = num_devs;
744 	concat->mtd.name = name;
745 
746 	concat->mtd._erase = concat_erase;
747 	concat->mtd._sync = concat_sync;
748 	concat->mtd._lock = concat_lock;
749 	concat->mtd._unlock = concat_unlock;
750 	concat->mtd._is_locked = concat_is_locked;
751 	concat->mtd._suspend = concat_suspend;
752 	concat->mtd._resume = concat_resume;
753 
754 	/*
755 	 * Combine the erase block size info of the subdevices:
756 	 *
757 	 * first, walk the map of the new device and see how
758 	 * many changes in erase size we have
759 	 */
760 	max_erasesize = curr_erasesize = subdev[0]->erasesize;
761 	num_erase_region = 1;
762 	for (i = 0; i < num_devs; i++) {
763 		if (subdev[i]->numeraseregions == 0) {
764 			/* current subdevice has uniform erase size */
765 			if (subdev[i]->erasesize != curr_erasesize) {
766 				/* if it differs from the last subdevice's erase size, count it */
767 				++num_erase_region;
768 				curr_erasesize = subdev[i]->erasesize;
769 				if (curr_erasesize > max_erasesize)
770 					max_erasesize = curr_erasesize;
771 			}
772 		} else {
773 			/* current subdevice has variable erase size */
774 			int j;
775 			for (j = 0; j < subdev[i]->numeraseregions; j++) {
776 
777 				/* walk the list of erase regions, count any changes */
778 				if (subdev[i]->eraseregions[j].erasesize !=
779 				    curr_erasesize) {
780 					++num_erase_region;
781 					curr_erasesize =
782 					    subdev[i]->eraseregions[j].
783 					    erasesize;
784 					if (curr_erasesize > max_erasesize)
785 						max_erasesize = curr_erasesize;
786 				}
787 			}
788 		}
789 	}
790 
791 	if (num_erase_region == 1) {
792 		/*
793 		 * All subdevices have the same uniform erase size.
794 		 * This is easy:
795 		 */
796 		concat->mtd.erasesize = curr_erasesize;
797 		concat->mtd.numeraseregions = 0;
798 	} else {
799 		uint64_t tmp64;
800 
801 		/*
802 		 * erase block size varies across the subdevices: allocate
803 		 * space to store the data describing the variable erase regions
804 		 */
805 		struct mtd_erase_region_info *erase_region_p;
806 		uint64_t begin, position;
807 
808 		concat->mtd.erasesize = max_erasesize;
809 		concat->mtd.numeraseregions = num_erase_region;
810 		concat->mtd.eraseregions = erase_region_p =
811 		    kmalloc_objs(struct mtd_erase_region_info, num_erase_region);
812 		if (!erase_region_p) {
813 			kfree(concat);
814 			printk
815 			    ("memory allocation error while creating erase region list"
816 			     " for device \"%s\"\n", name);
817 			return NULL;
818 		}
819 
820 		/*
821 		 * walk the map of the new device once more and fill in
822 		 * erase region info:
823 		 */
824 		curr_erasesize = subdev[0]->erasesize;
825 		begin = position = 0;
826 		for (i = 0; i < num_devs; i++) {
827 			if (subdev[i]->numeraseregions == 0) {
828 				/* current subdevice has uniform erase size */
829 				if (subdev[i]->erasesize != curr_erasesize) {
830 					/*
831 					 *  fill in an mtd_erase_region_info structure for the area
832 					 *  we have walked so far:
833 					 */
834 					erase_region_p->offset = begin;
835 					erase_region_p->erasesize =
836 					    curr_erasesize;
837 					tmp64 = position - begin;
838 					do_div(tmp64, curr_erasesize);
839 					erase_region_p->numblocks = tmp64;
840 					begin = position;
841 
842 					curr_erasesize = subdev[i]->erasesize;
843 					++erase_region_p;
844 				}
845 				position += subdev[i]->size;
846 			} else {
847 				/* current subdevice has variable erase size */
848 				int j;
849 				for (j = 0; j < subdev[i]->numeraseregions; j++) {
850 					/* walk the list of erase regions, count any changes */
851 					if (subdev[i]->eraseregions[j].
852 					    erasesize != curr_erasesize) {
853 						erase_region_p->offset = begin;
854 						erase_region_p->erasesize =
855 						    curr_erasesize;
856 						tmp64 = position - begin;
857 						do_div(tmp64, curr_erasesize);
858 						erase_region_p->numblocks = tmp64;
859 						begin = position;
860 
861 						curr_erasesize =
862 						    subdev[i]->eraseregions[j].
863 						    erasesize;
864 						++erase_region_p;
865 					}
866 					position +=
867 					    subdev[i]->eraseregions[j].
868 					    numblocks * (uint64_t)curr_erasesize;
869 				}
870 			}
871 		}
872 		/* Now write the final entry */
873 		erase_region_p->offset = begin;
874 		erase_region_p->erasesize = curr_erasesize;
875 		tmp64 = position - begin;
876 		do_div(tmp64, curr_erasesize);
877 		erase_region_p->numblocks = tmp64;
878 	}
879 
880 	return &concat->mtd;
881 }
882 
883 /* Cleans the context obtained from mtd_concat_create() */
884 void mtd_concat_destroy(struct mtd_info *mtd)
885 {
886 	struct mtd_concat *concat = CONCAT(mtd);
887 	if (concat->mtd.numeraseregions)
888 		kfree(concat->mtd.eraseregions);
889 	kfree(concat);
890 }
891 
892 EXPORT_SYMBOL(mtd_concat_create);
893 EXPORT_SYMBOL(mtd_concat_destroy);
894 
895 MODULE_LICENSE("GPL");
896 MODULE_AUTHOR("Robert Kaiser <rkaiser@sysgo.de>");
897 MODULE_DESCRIPTION("Generic support for concatenating of MTD devices");
898