xref: /openbmc/u-boot/drivers/mtd/mtdpart.c (revision 9450ab2ba8d720bd9f73bccc0af2e2b5a2c2aaf1)
183d290c5STom Rini // SPDX-License-Identifier: GPL-2.0+
2e29c22f5SKyungmin Park /*
3e29c22f5SKyungmin Park  * Simple MTD partitioning layer
4e29c22f5SKyungmin Park  *
5ff94bc40SHeiko Schocher  * Copyright © 2000 Nicolas Pitre <nico@fluxnic.net>
6ff94bc40SHeiko Schocher  * Copyright © 2002 Thomas Gleixner <gleixner@linutronix.de>
7ff94bc40SHeiko Schocher  * Copyright © 2000-2010 David Woodhouse <dwmw2@infradead.org>
8e29c22f5SKyungmin Park  *
9e29c22f5SKyungmin Park  */
10e29c22f5SKyungmin Park 
11ff94bc40SHeiko Schocher #ifndef __UBOOT__
12ff94bc40SHeiko Schocher #include <linux/module.h>
13ff94bc40SHeiko Schocher #include <linux/types.h>
14ff94bc40SHeiko Schocher #include <linux/kernel.h>
15ff94bc40SHeiko Schocher #include <linux/slab.h>
16ff94bc40SHeiko Schocher #include <linux/list.h>
17ff94bc40SHeiko Schocher #include <linux/kmod.h>
18ff94bc40SHeiko Schocher #endif
19ff94bc40SHeiko Schocher 
20e29c22f5SKyungmin Park #include <common.h>
21e29c22f5SKyungmin Park #include <malloc.h>
221221ce45SMasahiro Yamada #include <linux/errno.h>
23ff94bc40SHeiko Schocher #include <linux/compat.h>
24ff94bc40SHeiko Schocher #include <ubi_uboot.h>
25e29c22f5SKyungmin Park 
26e29c22f5SKyungmin Park #include <linux/mtd/mtd.h>
27e29c22f5SKyungmin Park #include <linux/mtd/partitions.h>
28ff94bc40SHeiko Schocher #include <linux/err.h>
2921cc1fb5SMiquel Raynal #include <linux/sizes.h>
30ff94bc40SHeiko Schocher 
31ff94bc40SHeiko Schocher #include "mtdcore.h"
32e29c22f5SKyungmin Park 
33ff94bc40SHeiko Schocher #ifndef __UBOOT__
34ff94bc40SHeiko Schocher static DEFINE_MUTEX(mtd_partitions_mutex);
35ff94bc40SHeiko Schocher #else
36ff94bc40SHeiko Schocher DEFINE_MUTEX(mtd_partitions_mutex);
37ff94bc40SHeiko Schocher #endif
38e29c22f5SKyungmin Park 
39ff94bc40SHeiko Schocher #ifdef __UBOOT__
40ff94bc40SHeiko Schocher /* from mm/util.c */
41ff94bc40SHeiko Schocher 
42ff94bc40SHeiko Schocher /**
43ff94bc40SHeiko Schocher  * kstrdup - allocate space for and copy an existing string
44ff94bc40SHeiko Schocher  * @s: the string to duplicate
45ff94bc40SHeiko Schocher  * @gfp: the GFP mask used in the kmalloc() call when allocating memory
46ff94bc40SHeiko Schocher  */
kstrdup(const char * s,gfp_t gfp)47ff94bc40SHeiko Schocher char *kstrdup(const char *s, gfp_t gfp)
48ff94bc40SHeiko Schocher {
49ff94bc40SHeiko Schocher 	size_t len;
50ff94bc40SHeiko Schocher 	char *buf;
51ff94bc40SHeiko Schocher 
52ff94bc40SHeiko Schocher 	if (!s)
53ff94bc40SHeiko Schocher 		return NULL;
54ff94bc40SHeiko Schocher 
55ff94bc40SHeiko Schocher 	len = strlen(s) + 1;
56ff94bc40SHeiko Schocher 	buf = kmalloc(len, gfp);
57ff94bc40SHeiko Schocher 	if (buf)
58ff94bc40SHeiko Schocher 		memcpy(buf, s, len);
59ff94bc40SHeiko Schocher 	return buf;
60ff94bc40SHeiko Schocher }
61ff94bc40SHeiko Schocher #endif
62ff94bc40SHeiko Schocher 
6321cc1fb5SMiquel Raynal #define MTD_SIZE_REMAINING		(~0LLU)
6421cc1fb5SMiquel Raynal #define MTD_OFFSET_NOT_SPECIFIED	(~0LLU)
6521cc1fb5SMiquel Raynal 
mtd_partitions_used(struct mtd_info * master)66*4a5594faSBoris Brezillon bool mtd_partitions_used(struct mtd_info *master)
67*4a5594faSBoris Brezillon {
68*4a5594faSBoris Brezillon 	struct mtd_info *slave;
69*4a5594faSBoris Brezillon 
70*4a5594faSBoris Brezillon 	list_for_each_entry(slave, &master->partitions, node) {
71*4a5594faSBoris Brezillon 		if (slave->usecount)
72*4a5594faSBoris Brezillon 			return true;
73*4a5594faSBoris Brezillon 	}
74*4a5594faSBoris Brezillon 
75*4a5594faSBoris Brezillon 	return false;
76*4a5594faSBoris Brezillon }
77*4a5594faSBoris Brezillon 
7821cc1fb5SMiquel Raynal /**
7921cc1fb5SMiquel Raynal  * mtd_parse_partition - Parse @mtdparts partition definition, fill @partition
8021cc1fb5SMiquel Raynal  *                       with it and update the @mtdparts string pointer.
8121cc1fb5SMiquel Raynal  *
8221cc1fb5SMiquel Raynal  * The partition name is allocated and must be freed by the caller.
8321cc1fb5SMiquel Raynal  *
8421cc1fb5SMiquel Raynal  * This function is widely inspired from part_parse (mtdparts.c).
8521cc1fb5SMiquel Raynal  *
8621cc1fb5SMiquel Raynal  * @mtdparts: String describing the partition with mtdparts command syntax
8721cc1fb5SMiquel Raynal  * @partition: MTD partition structure to fill
8821cc1fb5SMiquel Raynal  *
8921cc1fb5SMiquel Raynal  * @return 0 on success, an error otherwise.
9021cc1fb5SMiquel Raynal  */
mtd_parse_partition(const char ** _mtdparts,struct mtd_partition * partition)9121cc1fb5SMiquel Raynal static int mtd_parse_partition(const char **_mtdparts,
9221cc1fb5SMiquel Raynal 			       struct mtd_partition *partition)
9321cc1fb5SMiquel Raynal {
9421cc1fb5SMiquel Raynal 	const char *mtdparts = *_mtdparts;
9521cc1fb5SMiquel Raynal 	const char *name = NULL;
9621cc1fb5SMiquel Raynal 	int name_len;
9721cc1fb5SMiquel Raynal 	char *buf;
9821cc1fb5SMiquel Raynal 
9921cc1fb5SMiquel Raynal 	/* Ensure the partition structure is empty */
10021cc1fb5SMiquel Raynal 	memset(partition, 0, sizeof(struct mtd_partition));
10121cc1fb5SMiquel Raynal 
10221cc1fb5SMiquel Raynal 	/* Fetch the partition size */
10321cc1fb5SMiquel Raynal 	if (*mtdparts == '-') {
10421cc1fb5SMiquel Raynal 		/* Assign all remaining space to this partition */
10521cc1fb5SMiquel Raynal 		partition->size = MTD_SIZE_REMAINING;
10621cc1fb5SMiquel Raynal 		mtdparts++;
10721cc1fb5SMiquel Raynal 	} else {
10821cc1fb5SMiquel Raynal 		partition->size = ustrtoull(mtdparts, (char **)&mtdparts, 0);
10921cc1fb5SMiquel Raynal 		if (partition->size < SZ_4K) {
11021cc1fb5SMiquel Raynal 			printf("Minimum partition size 4kiB, %lldB requested\n",
11121cc1fb5SMiquel Raynal 			       partition->size);
11221cc1fb5SMiquel Raynal 			return -EINVAL;
11321cc1fb5SMiquel Raynal 		}
11421cc1fb5SMiquel Raynal 	}
11521cc1fb5SMiquel Raynal 
11621cc1fb5SMiquel Raynal 	/* Check for the offset */
11721cc1fb5SMiquel Raynal 	partition->offset = MTD_OFFSET_NOT_SPECIFIED;
11821cc1fb5SMiquel Raynal 	if (*mtdparts == '@') {
11921cc1fb5SMiquel Raynal 		mtdparts++;
12021cc1fb5SMiquel Raynal 		partition->offset = ustrtoull(mtdparts, (char **)&mtdparts, 0);
12121cc1fb5SMiquel Raynal 	}
12221cc1fb5SMiquel Raynal 
12321cc1fb5SMiquel Raynal 	/* Now look for the name */
12421cc1fb5SMiquel Raynal 	if (*mtdparts == '(') {
12521cc1fb5SMiquel Raynal 		name = ++mtdparts;
12621cc1fb5SMiquel Raynal 		mtdparts = strchr(name, ')');
12721cc1fb5SMiquel Raynal 		if (!mtdparts) {
12821cc1fb5SMiquel Raynal 			printf("No closing ')' found in partition name\n");
12921cc1fb5SMiquel Raynal 			return -EINVAL;
13021cc1fb5SMiquel Raynal 		}
13121cc1fb5SMiquel Raynal 		name_len = mtdparts - name + 1;
13221cc1fb5SMiquel Raynal 		if ((name_len - 1) == 0) {
13321cc1fb5SMiquel Raynal 			printf("Empty partition name\n");
13421cc1fb5SMiquel Raynal 			return -EINVAL;
13521cc1fb5SMiquel Raynal 		}
13621cc1fb5SMiquel Raynal 		mtdparts++;
13721cc1fb5SMiquel Raynal 	} else {
13821cc1fb5SMiquel Raynal 		/* Name will be of the form size@offset */
13921cc1fb5SMiquel Raynal 		name_len = 22;
14021cc1fb5SMiquel Raynal 	}
14121cc1fb5SMiquel Raynal 
14221cc1fb5SMiquel Raynal 	/* Check if the partition is read-only */
14321cc1fb5SMiquel Raynal 	if (strncmp(mtdparts, "ro", 2) == 0) {
14421cc1fb5SMiquel Raynal 		partition->mask_flags |= MTD_WRITEABLE;
14521cc1fb5SMiquel Raynal 		mtdparts += 2;
14621cc1fb5SMiquel Raynal 	}
14721cc1fb5SMiquel Raynal 
14821cc1fb5SMiquel Raynal 	/* Check for a potential next partition definition */
14921cc1fb5SMiquel Raynal 	if (*mtdparts == ',') {
15021cc1fb5SMiquel Raynal 		if (partition->size == MTD_SIZE_REMAINING) {
15121cc1fb5SMiquel Raynal 			printf("No partitions allowed after a fill-up\n");
15221cc1fb5SMiquel Raynal 			return -EINVAL;
15321cc1fb5SMiquel Raynal 		}
15421cc1fb5SMiquel Raynal 		++mtdparts;
15521cc1fb5SMiquel Raynal 	} else if ((*mtdparts == ';') || (*mtdparts == '\0')) {
15621cc1fb5SMiquel Raynal 		/* NOP */
15721cc1fb5SMiquel Raynal 	} else {
15821cc1fb5SMiquel Raynal 		printf("Unexpected character '%c' in mtdparts\n", *mtdparts);
15921cc1fb5SMiquel Raynal 		return -EINVAL;
16021cc1fb5SMiquel Raynal 	}
16121cc1fb5SMiquel Raynal 
16221cc1fb5SMiquel Raynal 	/*
16321cc1fb5SMiquel Raynal 	 * Allocate a buffer for the name and either copy the provided name or
16421cc1fb5SMiquel Raynal 	 * auto-generate it with the form 'size@offset'.
16521cc1fb5SMiquel Raynal 	 */
16621cc1fb5SMiquel Raynal 	buf = malloc(name_len);
16721cc1fb5SMiquel Raynal 	if (!buf)
16821cc1fb5SMiquel Raynal 		return -ENOMEM;
16921cc1fb5SMiquel Raynal 
17021cc1fb5SMiquel Raynal 	if (name)
17121cc1fb5SMiquel Raynal 		strncpy(buf, name, name_len - 1);
17221cc1fb5SMiquel Raynal 	else
17321cc1fb5SMiquel Raynal 		snprintf(buf, name_len, "0x%08llx@0x%08llx",
17421cc1fb5SMiquel Raynal 			 partition->size, partition->offset);
17521cc1fb5SMiquel Raynal 
17621cc1fb5SMiquel Raynal 	buf[name_len - 1] = '\0';
17721cc1fb5SMiquel Raynal 	partition->name = buf;
17821cc1fb5SMiquel Raynal 
17921cc1fb5SMiquel Raynal 	*_mtdparts = mtdparts;
18021cc1fb5SMiquel Raynal 
18121cc1fb5SMiquel Raynal 	return 0;
18221cc1fb5SMiquel Raynal }
18321cc1fb5SMiquel Raynal 
18421cc1fb5SMiquel Raynal /**
18521cc1fb5SMiquel Raynal  * mtd_parse_partitions - Create a partition array from an mtdparts definition
18621cc1fb5SMiquel Raynal  *
18721cc1fb5SMiquel Raynal  * Stateless function that takes a @parent MTD device, a string @_mtdparts
18821cc1fb5SMiquel Raynal  * describing the partitions (with the "mtdparts" command syntax) and creates
18921cc1fb5SMiquel Raynal  * the corresponding MTD partition structure array @_parts. Both the name and
19021cc1fb5SMiquel Raynal  * the structure partition itself must be freed freed, the caller may use
19121cc1fb5SMiquel Raynal  * @mtd_free_parsed_partitions() for this purpose.
19221cc1fb5SMiquel Raynal  *
19321cc1fb5SMiquel Raynal  * @parent: MTD device which contains the partitions
19421cc1fb5SMiquel Raynal  * @_mtdparts: Pointer to a string describing the partitions with "mtdparts"
19521cc1fb5SMiquel Raynal  *             command syntax.
19621cc1fb5SMiquel Raynal  * @_parts: Allocated array containing the partitions, must be freed by the
19721cc1fb5SMiquel Raynal  *          caller.
19821cc1fb5SMiquel Raynal  * @_nparts: Size of @_parts array.
19921cc1fb5SMiquel Raynal  *
20021cc1fb5SMiquel Raynal  * @return 0 on success, an error otherwise.
20121cc1fb5SMiquel Raynal  */
mtd_parse_partitions(struct mtd_info * parent,const char ** _mtdparts,struct mtd_partition ** _parts,int * _nparts)20221cc1fb5SMiquel Raynal int mtd_parse_partitions(struct mtd_info *parent, const char **_mtdparts,
20321cc1fb5SMiquel Raynal 			 struct mtd_partition **_parts, int *_nparts)
20421cc1fb5SMiquel Raynal {
20521cc1fb5SMiquel Raynal 	struct mtd_partition partition = {}, *parts;
20621cc1fb5SMiquel Raynal 	const char *mtdparts = *_mtdparts;
20721cc1fb5SMiquel Raynal 	int cur_off = 0, cur_sz = 0;
20821cc1fb5SMiquel Raynal 	int nparts = 0;
20921cc1fb5SMiquel Raynal 	int ret, idx;
21021cc1fb5SMiquel Raynal 	u64 sz;
21121cc1fb5SMiquel Raynal 
21221cc1fb5SMiquel Raynal 	/* First, iterate over the partitions until we know their number */
21321cc1fb5SMiquel Raynal 	while (mtdparts[0] != '\0' && mtdparts[0] != ';') {
21421cc1fb5SMiquel Raynal 		ret = mtd_parse_partition(&mtdparts, &partition);
21521cc1fb5SMiquel Raynal 		if (ret)
21621cc1fb5SMiquel Raynal 			return ret;
21721cc1fb5SMiquel Raynal 
21821cc1fb5SMiquel Raynal 		free((char *)partition.name);
21921cc1fb5SMiquel Raynal 		nparts++;
22021cc1fb5SMiquel Raynal 	}
22121cc1fb5SMiquel Raynal 
22221cc1fb5SMiquel Raynal 	/* Allocate an array of partitions to give back to the caller */
22321cc1fb5SMiquel Raynal 	parts = malloc(sizeof(*parts) * nparts);
22421cc1fb5SMiquel Raynal 	if (!parts) {
22521cc1fb5SMiquel Raynal 		printf("Not enough space to save partitions meta-data\n");
22621cc1fb5SMiquel Raynal 		return -ENOMEM;
22721cc1fb5SMiquel Raynal 	}
22821cc1fb5SMiquel Raynal 
22921cc1fb5SMiquel Raynal 	/* Iterate again over each partition to save the data in our array */
23021cc1fb5SMiquel Raynal 	for (idx = 0; idx < nparts; idx++) {
23121cc1fb5SMiquel Raynal 		ret = mtd_parse_partition(_mtdparts, &parts[idx]);
23221cc1fb5SMiquel Raynal 		if (ret)
23321cc1fb5SMiquel Raynal 			return ret;
23421cc1fb5SMiquel Raynal 
23521cc1fb5SMiquel Raynal 		if (parts[idx].size == MTD_SIZE_REMAINING)
23621cc1fb5SMiquel Raynal 			parts[idx].size = parent->size - cur_sz;
23721cc1fb5SMiquel Raynal 		cur_sz += parts[idx].size;
23821cc1fb5SMiquel Raynal 
23921cc1fb5SMiquel Raynal 		sz = parts[idx].size;
24021cc1fb5SMiquel Raynal 		if (sz < parent->writesize || do_div(sz, parent->writesize)) {
24121cc1fb5SMiquel Raynal 			printf("Partition size must be a multiple of %d\n",
24221cc1fb5SMiquel Raynal 			       parent->writesize);
24321cc1fb5SMiquel Raynal 			return -EINVAL;
24421cc1fb5SMiquel Raynal 		}
24521cc1fb5SMiquel Raynal 
24621cc1fb5SMiquel Raynal 		if (parts[idx].offset == MTD_OFFSET_NOT_SPECIFIED)
24721cc1fb5SMiquel Raynal 			parts[idx].offset = cur_off;
24821cc1fb5SMiquel Raynal 		cur_off += parts[idx].size;
24921cc1fb5SMiquel Raynal 
25021cc1fb5SMiquel Raynal 		parts[idx].ecclayout = parent->ecclayout;
25121cc1fb5SMiquel Raynal 	}
25221cc1fb5SMiquel Raynal 
25321cc1fb5SMiquel Raynal 	/* Offset by one mtdparts to point to the next device if any */
25421cc1fb5SMiquel Raynal 	if (*_mtdparts[0] == ';')
25521cc1fb5SMiquel Raynal 		(*_mtdparts)++;
25621cc1fb5SMiquel Raynal 
25721cc1fb5SMiquel Raynal 	*_parts = parts;
25821cc1fb5SMiquel Raynal 	*_nparts = nparts;
25921cc1fb5SMiquel Raynal 
26021cc1fb5SMiquel Raynal 	return 0;
26121cc1fb5SMiquel Raynal }
26221cc1fb5SMiquel Raynal 
26321cc1fb5SMiquel Raynal /**
26421cc1fb5SMiquel Raynal  * mtd_free_parsed_partitions - Free dynamically allocated partitions
26521cc1fb5SMiquel Raynal  *
26621cc1fb5SMiquel Raynal  * Each successful call to @mtd_parse_partitions must be followed by a call to
26721cc1fb5SMiquel Raynal  * @mtd_free_parsed_partitions to free any allocated array during the parsing
26821cc1fb5SMiquel Raynal  * process.
26921cc1fb5SMiquel Raynal  *
27021cc1fb5SMiquel Raynal  * @parts: Array containing the partitions that will be freed.
27121cc1fb5SMiquel Raynal  * @nparts: Size of @parts array.
27221cc1fb5SMiquel Raynal  */
mtd_free_parsed_partitions(struct mtd_partition * parts,unsigned int nparts)27321cc1fb5SMiquel Raynal void mtd_free_parsed_partitions(struct mtd_partition *parts,
27421cc1fb5SMiquel Raynal 				unsigned int nparts)
27521cc1fb5SMiquel Raynal {
27621cc1fb5SMiquel Raynal 	int i;
27721cc1fb5SMiquel Raynal 
27821cc1fb5SMiquel Raynal 	for (i = 0; i < nparts; i++)
27921cc1fb5SMiquel Raynal 		free((char *)parts[i].name);
28021cc1fb5SMiquel Raynal 
28121cc1fb5SMiquel Raynal 	free(parts);
28221cc1fb5SMiquel Raynal }
28321cc1fb5SMiquel Raynal 
284e29c22f5SKyungmin Park /*
285e29c22f5SKyungmin Park  * MTD methods which simply translate the effective address and pass through
286e29c22f5SKyungmin Park  * to the _real_ device.
287e29c22f5SKyungmin Park  */
288e29c22f5SKyungmin Park 
part_read(struct mtd_info * mtd,loff_t from,size_t len,size_t * retlen,u_char * buf)289e29c22f5SKyungmin Park static int part_read(struct mtd_info *mtd, loff_t from, size_t len,
290e29c22f5SKyungmin Park 		size_t *retlen, u_char *buf)
291e29c22f5SKyungmin Park {
2928d2effeaSStefan Roese 	struct mtd_ecc_stats stats;
293e29c22f5SKyungmin Park 	int res;
294e29c22f5SKyungmin Park 
2952a74930dSMiquel Raynal 	stats = mtd->parent->ecc_stats;
2962a74930dSMiquel Raynal 	res = mtd->parent->_read(mtd->parent, from + mtd->offset, len,
297ff94bc40SHeiko Schocher 				 retlen, buf);
29840462e54SPaul Burton 	if (unlikely(mtd_is_eccerr(res)))
29940462e54SPaul Burton 		mtd->ecc_stats.failed +=
3002a74930dSMiquel Raynal 			mtd->parent->ecc_stats.failed - stats.failed;
30140462e54SPaul Burton 	else
30240462e54SPaul Burton 		mtd->ecc_stats.corrected +=
3032a74930dSMiquel Raynal 			mtd->parent->ecc_stats.corrected - stats.corrected;
304e29c22f5SKyungmin Park 	return res;
305e29c22f5SKyungmin Park }
306e29c22f5SKyungmin Park 
307ff94bc40SHeiko Schocher #ifndef __UBOOT__
part_point(struct mtd_info * mtd,loff_t from,size_t len,size_t * retlen,void ** virt,resource_size_t * phys)308ff94bc40SHeiko Schocher static int part_point(struct mtd_info *mtd, loff_t from, size_t len,
309ff94bc40SHeiko Schocher 		size_t *retlen, void **virt, resource_size_t *phys)
310ff94bc40SHeiko Schocher {
3112a74930dSMiquel Raynal 	return mtd->parent->_point(mtd->parent, from + mtd->offset, len,
312ff94bc40SHeiko Schocher 				   retlen, virt, phys);
313ff94bc40SHeiko Schocher }
314ff94bc40SHeiko Schocher 
part_unpoint(struct mtd_info * mtd,loff_t from,size_t len)315ff94bc40SHeiko Schocher static int part_unpoint(struct mtd_info *mtd, loff_t from, size_t len)
316ff94bc40SHeiko Schocher {
3172a74930dSMiquel Raynal 	return mtd->parent->_unpoint(mtd->parent, from + mtd->offset, len);
318ff94bc40SHeiko Schocher }
319ff94bc40SHeiko Schocher #endif
320ff94bc40SHeiko Schocher 
part_get_unmapped_area(struct mtd_info * mtd,unsigned long len,unsigned long offset,unsigned long flags)321ff94bc40SHeiko Schocher static unsigned long part_get_unmapped_area(struct mtd_info *mtd,
322ff94bc40SHeiko Schocher 					    unsigned long len,
323ff94bc40SHeiko Schocher 					    unsigned long offset,
324ff94bc40SHeiko Schocher 					    unsigned long flags)
325ff94bc40SHeiko Schocher {
3262a74930dSMiquel Raynal 	offset += mtd->offset;
3272a74930dSMiquel Raynal 	return mtd->parent->_get_unmapped_area(mtd->parent, len, offset, flags);
328ff94bc40SHeiko Schocher }
329ff94bc40SHeiko Schocher 
part_read_oob(struct mtd_info * mtd,loff_t from,struct mtd_oob_ops * ops)330e29c22f5SKyungmin Park static int part_read_oob(struct mtd_info *mtd, loff_t from,
331e29c22f5SKyungmin Park 		struct mtd_oob_ops *ops)
332e29c22f5SKyungmin Park {
333e29c22f5SKyungmin Park 	int res;
334e29c22f5SKyungmin Park 
335e29c22f5SKyungmin Park 	if (from >= mtd->size)
336e29c22f5SKyungmin Park 		return -EINVAL;
337e29c22f5SKyungmin Park 	if (ops->datbuf && from + ops->len > mtd->size)
338e29c22f5SKyungmin Park 		return -EINVAL;
339e29c22f5SKyungmin Park 
340ff94bc40SHeiko Schocher 	/*
341ff94bc40SHeiko Schocher 	 * If OOB is also requested, make sure that we do not read past the end
342ff94bc40SHeiko Schocher 	 * of this partition.
343ff94bc40SHeiko Schocher 	 */
344ff94bc40SHeiko Schocher 	if (ops->oobbuf) {
345ff94bc40SHeiko Schocher 		size_t len, pages;
346ff94bc40SHeiko Schocher 
347ff94bc40SHeiko Schocher 		if (ops->mode == MTD_OPS_AUTO_OOB)
348ff94bc40SHeiko Schocher 			len = mtd->oobavail;
349ff94bc40SHeiko Schocher 		else
350ff94bc40SHeiko Schocher 			len = mtd->oobsize;
351ff94bc40SHeiko Schocher 		pages = mtd_div_by_ws(mtd->size, mtd);
352ff94bc40SHeiko Schocher 		pages -= mtd_div_by_ws(from, mtd);
353ff94bc40SHeiko Schocher 		if (ops->ooboffs + ops->ooblen > pages * len)
354ff94bc40SHeiko Schocher 			return -EINVAL;
355ff94bc40SHeiko Schocher 	}
356ff94bc40SHeiko Schocher 
3572a74930dSMiquel Raynal 	res = mtd->parent->_read_oob(mtd->parent, from + mtd->offset, ops);
358e29c22f5SKyungmin Park 	if (unlikely(res)) {
359dfe64e2cSSergey Lapin 		if (mtd_is_bitflip(res))
360e29c22f5SKyungmin Park 			mtd->ecc_stats.corrected++;
361dfe64e2cSSergey Lapin 		if (mtd_is_eccerr(res))
362e29c22f5SKyungmin Park 			mtd->ecc_stats.failed++;
363e29c22f5SKyungmin Park 	}
364e29c22f5SKyungmin Park 	return res;
365e29c22f5SKyungmin Park }
366e29c22f5SKyungmin Park 
part_read_user_prot_reg(struct mtd_info * mtd,loff_t from,size_t len,size_t * retlen,u_char * buf)3678d2effeaSStefan Roese static int part_read_user_prot_reg(struct mtd_info *mtd, loff_t from,
3688d2effeaSStefan Roese 		size_t len, size_t *retlen, u_char *buf)
369e29c22f5SKyungmin Park {
3702a74930dSMiquel Raynal 	return mtd->parent->_read_user_prot_reg(mtd->parent, from, len,
371ff94bc40SHeiko Schocher 						retlen, buf);
372e29c22f5SKyungmin Park }
373e29c22f5SKyungmin Park 
part_get_user_prot_info(struct mtd_info * mtd,size_t len,size_t * retlen,struct otp_info * buf)3744e67c571SHeiko Schocher static int part_get_user_prot_info(struct mtd_info *mtd, size_t len,
3754e67c571SHeiko Schocher 				   size_t *retlen, struct otp_info *buf)
376e29c22f5SKyungmin Park {
3772a74930dSMiquel Raynal 	return mtd->parent->_get_user_prot_info(mtd->parent, len, retlen,
3784e67c571SHeiko Schocher 						buf);
379e29c22f5SKyungmin Park }
380e29c22f5SKyungmin Park 
part_read_fact_prot_reg(struct mtd_info * mtd,loff_t from,size_t len,size_t * retlen,u_char * buf)3818d2effeaSStefan Roese static int part_read_fact_prot_reg(struct mtd_info *mtd, loff_t from,
3828d2effeaSStefan Roese 		size_t len, size_t *retlen, u_char *buf)
383e29c22f5SKyungmin Park {
3842a74930dSMiquel Raynal 	return mtd->parent->_read_fact_prot_reg(mtd->parent, from, len,
385ff94bc40SHeiko Schocher 						retlen, buf);
386e29c22f5SKyungmin Park }
387e29c22f5SKyungmin Park 
part_get_fact_prot_info(struct mtd_info * mtd,size_t len,size_t * retlen,struct otp_info * buf)3884e67c571SHeiko Schocher static int part_get_fact_prot_info(struct mtd_info *mtd, size_t len,
3894e67c571SHeiko Schocher 				   size_t *retlen, struct otp_info *buf)
390e29c22f5SKyungmin Park {
3912a74930dSMiquel Raynal 	return mtd->parent->_get_fact_prot_info(mtd->parent, len, retlen,
3924e67c571SHeiko Schocher 						buf);
393e29c22f5SKyungmin Park }
394e29c22f5SKyungmin Park 
part_write(struct mtd_info * mtd,loff_t to,size_t len,size_t * retlen,const u_char * buf)395e29c22f5SKyungmin Park static int part_write(struct mtd_info *mtd, loff_t to, size_t len,
396e29c22f5SKyungmin Park 		size_t *retlen, const u_char *buf)
397e29c22f5SKyungmin Park {
3982a74930dSMiquel Raynal 	return mtd->parent->_write(mtd->parent, to + mtd->offset, len,
399ff94bc40SHeiko Schocher 				   retlen, buf);
400ff94bc40SHeiko Schocher }
401ff94bc40SHeiko Schocher 
part_panic_write(struct mtd_info * mtd,loff_t to,size_t len,size_t * retlen,const u_char * buf)402ff94bc40SHeiko Schocher static int part_panic_write(struct mtd_info *mtd, loff_t to, size_t len,
403ff94bc40SHeiko Schocher 		size_t *retlen, const u_char *buf)
404ff94bc40SHeiko Schocher {
4052a74930dSMiquel Raynal 	return mtd->parent->_panic_write(mtd->parent, to + mtd->offset, len,
406ff94bc40SHeiko Schocher 					 retlen, buf);
407e29c22f5SKyungmin Park }
408e29c22f5SKyungmin Park 
part_write_oob(struct mtd_info * mtd,loff_t to,struct mtd_oob_ops * ops)409e29c22f5SKyungmin Park static int part_write_oob(struct mtd_info *mtd, loff_t to,
410e29c22f5SKyungmin Park 		struct mtd_oob_ops *ops)
411e29c22f5SKyungmin Park {
412e29c22f5SKyungmin Park 	if (to >= mtd->size)
413e29c22f5SKyungmin Park 		return -EINVAL;
414e29c22f5SKyungmin Park 	if (ops->datbuf && to + ops->len > mtd->size)
415e29c22f5SKyungmin Park 		return -EINVAL;
4162a74930dSMiquel Raynal 	return mtd->parent->_write_oob(mtd->parent, to + mtd->offset, ops);
417e29c22f5SKyungmin Park }
418e29c22f5SKyungmin Park 
part_write_user_prot_reg(struct mtd_info * mtd,loff_t from,size_t len,size_t * retlen,u_char * buf)4198d2effeaSStefan Roese static int part_write_user_prot_reg(struct mtd_info *mtd, loff_t from,
4208d2effeaSStefan Roese 		size_t len, size_t *retlen, u_char *buf)
421e29c22f5SKyungmin Park {
4222a74930dSMiquel Raynal 	return mtd->parent->_write_user_prot_reg(mtd->parent, from, len,
423ff94bc40SHeiko Schocher 						 retlen, buf);
424e29c22f5SKyungmin Park }
425e29c22f5SKyungmin Park 
part_lock_user_prot_reg(struct mtd_info * mtd,loff_t from,size_t len)4268d2effeaSStefan Roese static int part_lock_user_prot_reg(struct mtd_info *mtd, loff_t from,
4278d2effeaSStefan Roese 		size_t len)
428e29c22f5SKyungmin Park {
4292a74930dSMiquel Raynal 	return mtd->parent->_lock_user_prot_reg(mtd->parent, from, len);
430e29c22f5SKyungmin Park }
431e29c22f5SKyungmin Park 
432ff94bc40SHeiko Schocher #ifndef __UBOOT__
part_writev(struct mtd_info * mtd,const struct kvec * vecs,unsigned long count,loff_t to,size_t * retlen)433ff94bc40SHeiko Schocher static int part_writev(struct mtd_info *mtd, const struct kvec *vecs,
434ff94bc40SHeiko Schocher 		unsigned long count, loff_t to, size_t *retlen)
435ff94bc40SHeiko Schocher {
4362a74930dSMiquel Raynal 	return mtd->parent->_writev(mtd->parent, vecs, count,
4372a74930dSMiquel Raynal 				    to + mtd->offset, retlen);
438ff94bc40SHeiko Schocher }
439ff94bc40SHeiko Schocher #endif
440ff94bc40SHeiko Schocher 
part_erase(struct mtd_info * mtd,struct erase_info * instr)441e29c22f5SKyungmin Park static int part_erase(struct mtd_info *mtd, struct erase_info *instr)
442e29c22f5SKyungmin Park {
443e29c22f5SKyungmin Park 	int ret;
444dfe64e2cSSergey Lapin 
4452a74930dSMiquel Raynal 	instr->addr += mtd->offset;
4462a74930dSMiquel Raynal 	ret = mtd->parent->_erase(mtd->parent, instr);
447e29c22f5SKyungmin Park 	if (ret) {
4488d2effeaSStefan Roese 		if (instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN)
4492a74930dSMiquel Raynal 			instr->fail_addr -= mtd->offset;
4502a74930dSMiquel Raynal 		instr->addr -= mtd->offset;
451e29c22f5SKyungmin Park 	}
452e29c22f5SKyungmin Park 	return ret;
453e29c22f5SKyungmin Park }
454e29c22f5SKyungmin Park 
mtd_erase_callback(struct erase_info * instr)455e29c22f5SKyungmin Park void mtd_erase_callback(struct erase_info *instr)
456e29c22f5SKyungmin Park {
457dfe64e2cSSergey Lapin 	if (instr->mtd->_erase == part_erase) {
4588d2effeaSStefan Roese 		if (instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN)
4592a74930dSMiquel Raynal 			instr->fail_addr -= instr->mtd->offset;
4602a74930dSMiquel Raynal 		instr->addr -= instr->mtd->offset;
461e29c22f5SKyungmin Park 	}
462e29c22f5SKyungmin Park 	if (instr->callback)
463e29c22f5SKyungmin Park 		instr->callback(instr);
464e29c22f5SKyungmin Park }
465ff94bc40SHeiko Schocher EXPORT_SYMBOL_GPL(mtd_erase_callback);
466e29c22f5SKyungmin Park 
part_lock(struct mtd_info * mtd,loff_t ofs,uint64_t len)4678d2effeaSStefan Roese static int part_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
468e29c22f5SKyungmin Park {
4692a74930dSMiquel Raynal 	return mtd->parent->_lock(mtd->parent, ofs + mtd->offset, len);
470e29c22f5SKyungmin Park }
471e29c22f5SKyungmin Park 
part_unlock(struct mtd_info * mtd,loff_t ofs,uint64_t len)4728d2effeaSStefan Roese static int part_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
473e29c22f5SKyungmin Park {
4742a74930dSMiquel Raynal 	return mtd->parent->_unlock(mtd->parent, ofs + mtd->offset, len);
475ff94bc40SHeiko Schocher }
476ff94bc40SHeiko Schocher 
part_is_locked(struct mtd_info * mtd,loff_t ofs,uint64_t len)477ff94bc40SHeiko Schocher static int part_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len)
478ff94bc40SHeiko Schocher {
4792a74930dSMiquel Raynal 	return mtd->parent->_is_locked(mtd->parent, ofs + mtd->offset, len);
480e29c22f5SKyungmin Park }
481e29c22f5SKyungmin Park 
part_sync(struct mtd_info * mtd)482e29c22f5SKyungmin Park static void part_sync(struct mtd_info *mtd)
483e29c22f5SKyungmin Park {
4842a74930dSMiquel Raynal 	mtd->parent->_sync(mtd->parent);
485e29c22f5SKyungmin Park }
486e29c22f5SKyungmin Park 
487ff94bc40SHeiko Schocher #ifndef __UBOOT__
part_suspend(struct mtd_info * mtd)488ff94bc40SHeiko Schocher static int part_suspend(struct mtd_info *mtd)
489ff94bc40SHeiko Schocher {
4902a74930dSMiquel Raynal 	return mtd->parent->_suspend(mtd->parent);
491ff94bc40SHeiko Schocher }
492ff94bc40SHeiko Schocher 
part_resume(struct mtd_info * mtd)493ff94bc40SHeiko Schocher static void part_resume(struct mtd_info *mtd)
494ff94bc40SHeiko Schocher {
4952a74930dSMiquel Raynal 	mtd->parent->_resume(mtd->parent);
496ff94bc40SHeiko Schocher }
497ff94bc40SHeiko Schocher #endif
498ff94bc40SHeiko Schocher 
part_block_isreserved(struct mtd_info * mtd,loff_t ofs)49986a720aaSEzequiel Garcia static int part_block_isreserved(struct mtd_info *mtd, loff_t ofs)
50086a720aaSEzequiel Garcia {
5012a74930dSMiquel Raynal 	ofs += mtd->offset;
5022a74930dSMiquel Raynal 	return mtd->parent->_block_isreserved(mtd->parent, ofs);
50386a720aaSEzequiel Garcia }
50486a720aaSEzequiel Garcia 
part_block_isbad(struct mtd_info * mtd,loff_t ofs)505e29c22f5SKyungmin Park static int part_block_isbad(struct mtd_info *mtd, loff_t ofs)
506e29c22f5SKyungmin Park {
5072a74930dSMiquel Raynal 	ofs += mtd->offset;
5082a74930dSMiquel Raynal 	return mtd->parent->_block_isbad(mtd->parent, ofs);
509e29c22f5SKyungmin Park }
510e29c22f5SKyungmin Park 
part_block_markbad(struct mtd_info * mtd,loff_t ofs)511e29c22f5SKyungmin Park static int part_block_markbad(struct mtd_info *mtd, loff_t ofs)
512e29c22f5SKyungmin Park {
513e29c22f5SKyungmin Park 	int res;
514e29c22f5SKyungmin Park 
5152a74930dSMiquel Raynal 	ofs += mtd->offset;
5162a74930dSMiquel Raynal 	res = mtd->parent->_block_markbad(mtd->parent, ofs);
517e29c22f5SKyungmin Park 	if (!res)
518e29c22f5SKyungmin Park 		mtd->ecc_stats.badblocks++;
519e29c22f5SKyungmin Park 	return res;
520e29c22f5SKyungmin Park }
521e29c22f5SKyungmin Park 
free_partition(struct mtd_info * p)5222a74930dSMiquel Raynal static inline void free_partition(struct mtd_info *p)
523ff94bc40SHeiko Schocher {
5242a74930dSMiquel Raynal 	kfree(p->name);
525ff94bc40SHeiko Schocher 	kfree(p);
526ff94bc40SHeiko Schocher }
527ff94bc40SHeiko Schocher 
528e29c22f5SKyungmin Park /*
529e29c22f5SKyungmin Park  * This function unregisters and destroy all slave MTD objects which are
5302a74930dSMiquel Raynal  * attached to the given master MTD object, recursively.
531e29c22f5SKyungmin Park  */
do_del_mtd_partitions(struct mtd_info * master)5322a74930dSMiquel Raynal static int do_del_mtd_partitions(struct mtd_info *master)
533e29c22f5SKyungmin Park {
5342a74930dSMiquel Raynal 	struct mtd_info *slave, *next;
535ff94bc40SHeiko Schocher 	int ret, err = 0;
536e29c22f5SKyungmin Park 
5372a74930dSMiquel Raynal 	list_for_each_entry_safe(slave, next, &master->partitions, node) {
5382a74930dSMiquel Raynal 		if (mtd_has_partitions(slave))
5392a74930dSMiquel Raynal 			del_mtd_partitions(slave);
540b0036f70SMiquel Raynal 
5412a74930dSMiquel Raynal 		debug("Deleting %s MTD partition\n", slave->name);
5422a74930dSMiquel Raynal 		ret = del_mtd_device(slave);
543ff94bc40SHeiko Schocher 		if (ret < 0) {
5442a74930dSMiquel Raynal 			printf("Error when deleting partition \"%s\" (%d)\n",
5452a74930dSMiquel Raynal 			       slave->name, ret);
546ff94bc40SHeiko Schocher 			err = ret;
547ff94bc40SHeiko Schocher 			continue;
548ff94bc40SHeiko Schocher 		}
5492a74930dSMiquel Raynal 
5502a74930dSMiquel Raynal 		list_del(&slave->node);
551ff94bc40SHeiko Schocher 		free_partition(slave);
552ff94bc40SHeiko Schocher 	}
553ff94bc40SHeiko Schocher 
554ff94bc40SHeiko Schocher 	return err;
555e29c22f5SKyungmin Park }
556e29c22f5SKyungmin Park 
del_mtd_partitions(struct mtd_info * master)5572a74930dSMiquel Raynal int del_mtd_partitions(struct mtd_info *master)
558e29c22f5SKyungmin Park {
5592a74930dSMiquel Raynal 	int ret;
5602a74930dSMiquel Raynal 
5612a74930dSMiquel Raynal 	debug("Deleting MTD partitions on \"%s\":\n", master->name);
5622a74930dSMiquel Raynal 
5632a74930dSMiquel Raynal 	mutex_lock(&mtd_partitions_mutex);
5642a74930dSMiquel Raynal 	ret = do_del_mtd_partitions(master);
5652a74930dSMiquel Raynal 	mutex_unlock(&mtd_partitions_mutex);
5662a74930dSMiquel Raynal 
5672a74930dSMiquel Raynal 	return ret;
5682a74930dSMiquel Raynal }
5692a74930dSMiquel Raynal 
allocate_partition(struct mtd_info * master,const struct mtd_partition * part,int partno,uint64_t cur_offset)5702a74930dSMiquel Raynal static struct mtd_info *allocate_partition(struct mtd_info *master,
5712a74930dSMiquel Raynal 					   const struct mtd_partition *part,
5722a74930dSMiquel Raynal 					   int partno, uint64_t cur_offset)
5732a74930dSMiquel Raynal {
5742a74930dSMiquel Raynal 	struct mtd_info *slave;
575ff94bc40SHeiko Schocher 	char *name;
576e29c22f5SKyungmin Park 
577e29c22f5SKyungmin Park 	/* allocate the partition structure */
578e29c22f5SKyungmin Park 	slave = kzalloc(sizeof(*slave), GFP_KERNEL);
579ff94bc40SHeiko Schocher 	name = kstrdup(part->name, GFP_KERNEL);
580ff94bc40SHeiko Schocher 	if (!name || !slave) {
5818d2effeaSStefan Roese 		printk(KERN_ERR"memory allocation error while creating partitions for \"%s\"\n",
582e29c22f5SKyungmin Park 		       master->name);
583ff94bc40SHeiko Schocher 		kfree(name);
584ff94bc40SHeiko Schocher 		kfree(slave);
585ff94bc40SHeiko Schocher 		return ERR_PTR(-ENOMEM);
586e29c22f5SKyungmin Park 	}
587e29c22f5SKyungmin Park 
588e29c22f5SKyungmin Park 	/* set up the MTD object for this partition */
5892a74930dSMiquel Raynal 	slave->type = master->type;
5902a74930dSMiquel Raynal 	slave->flags = master->flags & ~part->mask_flags;
5912a74930dSMiquel Raynal 	slave->size = part->size;
5922a74930dSMiquel Raynal 	slave->writesize = master->writesize;
5932a74930dSMiquel Raynal 	slave->writebufsize = master->writebufsize;
5942a74930dSMiquel Raynal 	slave->oobsize = master->oobsize;
5952a74930dSMiquel Raynal 	slave->oobavail = master->oobavail;
5962a74930dSMiquel Raynal 	slave->subpage_sft = master->subpage_sft;
597e29c22f5SKyungmin Park 
5982a74930dSMiquel Raynal 	slave->name = name;
5992a74930dSMiquel Raynal 	slave->owner = master->owner;
600ff94bc40SHeiko Schocher #ifndef __UBOOT__
6012a74930dSMiquel Raynal 	slave->backing_dev_info = master->backing_dev_info;
602ff94bc40SHeiko Schocher 
603ff94bc40SHeiko Schocher 	/* NOTE:  we don't arrange MTDs as a tree; it'd be error-prone
604ff94bc40SHeiko Schocher 	 * to have the same data be in two different partitions.
605ff94bc40SHeiko Schocher 	 */
6062a74930dSMiquel Raynal 	slave->dev.parent = master->dev.parent;
607ff94bc40SHeiko Schocher #endif
608e29c22f5SKyungmin Park 
609596cf083SBoris Brezillon 	if (master->_read)
6102a74930dSMiquel Raynal 		slave->_read = part_read;
611596cf083SBoris Brezillon 	if (master->_write)
6122a74930dSMiquel Raynal 		slave->_write = part_write;
613e29c22f5SKyungmin Park 
614ff94bc40SHeiko Schocher 	if (master->_panic_write)
6152a74930dSMiquel Raynal 		slave->_panic_write = part_panic_write;
616ff94bc40SHeiko Schocher 
617ff94bc40SHeiko Schocher #ifndef __UBOOT__
618ff94bc40SHeiko Schocher 	if (master->_point && master->_unpoint) {
6192a74930dSMiquel Raynal 		slave->_point = part_point;
6202a74930dSMiquel Raynal 		slave->_unpoint = part_unpoint;
621ff94bc40SHeiko Schocher 	}
622ff94bc40SHeiko Schocher #endif
623ff94bc40SHeiko Schocher 
624ff94bc40SHeiko Schocher 	if (master->_get_unmapped_area)
6252a74930dSMiquel Raynal 		slave->_get_unmapped_area = part_get_unmapped_area;
626dfe64e2cSSergey Lapin 	if (master->_read_oob)
6272a74930dSMiquel Raynal 		slave->_read_oob = part_read_oob;
628dfe64e2cSSergey Lapin 	if (master->_write_oob)
6292a74930dSMiquel Raynal 		slave->_write_oob = part_write_oob;
630dfe64e2cSSergey Lapin 	if (master->_read_user_prot_reg)
6312a74930dSMiquel Raynal 		slave->_read_user_prot_reg = part_read_user_prot_reg;
632dfe64e2cSSergey Lapin 	if (master->_read_fact_prot_reg)
6332a74930dSMiquel Raynal 		slave->_read_fact_prot_reg = part_read_fact_prot_reg;
634dfe64e2cSSergey Lapin 	if (master->_write_user_prot_reg)
6352a74930dSMiquel Raynal 		slave->_write_user_prot_reg = part_write_user_prot_reg;
636dfe64e2cSSergey Lapin 	if (master->_lock_user_prot_reg)
6372a74930dSMiquel Raynal 		slave->_lock_user_prot_reg = part_lock_user_prot_reg;
638dfe64e2cSSergey Lapin 	if (master->_get_user_prot_info)
6392a74930dSMiquel Raynal 		slave->_get_user_prot_info = part_get_user_prot_info;
640dfe64e2cSSergey Lapin 	if (master->_get_fact_prot_info)
6412a74930dSMiquel Raynal 		slave->_get_fact_prot_info = part_get_fact_prot_info;
642dfe64e2cSSergey Lapin 	if (master->_sync)
6432a74930dSMiquel Raynal 		slave->_sync = part_sync;
644ff94bc40SHeiko Schocher #ifndef __UBOOT__
645ff94bc40SHeiko Schocher 	if (!partno && !master->dev.class && master->_suspend &&
646ff94bc40SHeiko Schocher 	    master->_resume) {
6472a74930dSMiquel Raynal 		slave->_suspend = part_suspend;
6482a74930dSMiquel Raynal 		slave->_resume = part_resume;
649ff94bc40SHeiko Schocher 	}
650ff94bc40SHeiko Schocher 	if (master->_writev)
6512a74930dSMiquel Raynal 		slave->_writev = part_writev;
652ff94bc40SHeiko Schocher #endif
653dfe64e2cSSergey Lapin 	if (master->_lock)
6542a74930dSMiquel Raynal 		slave->_lock = part_lock;
655dfe64e2cSSergey Lapin 	if (master->_unlock)
6562a74930dSMiquel Raynal 		slave->_unlock = part_unlock;
657ff94bc40SHeiko Schocher 	if (master->_is_locked)
6582a74930dSMiquel Raynal 		slave->_is_locked = part_is_locked;
65986a720aaSEzequiel Garcia 	if (master->_block_isreserved)
6602a74930dSMiquel Raynal 		slave->_block_isreserved = part_block_isreserved;
661dfe64e2cSSergey Lapin 	if (master->_block_isbad)
6622a74930dSMiquel Raynal 		slave->_block_isbad = part_block_isbad;
663dfe64e2cSSergey Lapin 	if (master->_block_markbad)
6642a74930dSMiquel Raynal 		slave->_block_markbad = part_block_markbad;
6652a74930dSMiquel Raynal 	slave->_erase = part_erase;
6662a74930dSMiquel Raynal 	slave->parent = master;
6678d2effeaSStefan Roese 	slave->offset = part->offset;
6682a74930dSMiquel Raynal 	INIT_LIST_HEAD(&slave->partitions);
6692a74930dSMiquel Raynal 	INIT_LIST_HEAD(&slave->node);
670e29c22f5SKyungmin Park 
671e29c22f5SKyungmin Park 	if (slave->offset == MTDPART_OFS_APPEND)
672e29c22f5SKyungmin Park 		slave->offset = cur_offset;
673e29c22f5SKyungmin Park 	if (slave->offset == MTDPART_OFS_NXTBLK) {
674e29c22f5SKyungmin Park 		slave->offset = cur_offset;
6758d2effeaSStefan Roese 		if (mtd_mod_by_eb(cur_offset, master) != 0) {
676e29c22f5SKyungmin Park 			/* Round up to next erasesize */
6778d2effeaSStefan Roese 			slave->offset = (mtd_div_by_eb(cur_offset, master) + 1) * master->erasesize;
678ff94bc40SHeiko Schocher 			debug("Moving partition %d: "
679ff94bc40SHeiko Schocher 			       "0x%012llx -> 0x%012llx\n", partno,
680ff94bc40SHeiko Schocher 			       (unsigned long long)cur_offset, (unsigned long long)slave->offset);
681ff94bc40SHeiko Schocher 		}
682ff94bc40SHeiko Schocher 	}
683ff94bc40SHeiko Schocher 	if (slave->offset == MTDPART_OFS_RETAIN) {
684ff94bc40SHeiko Schocher 		slave->offset = cur_offset;
6852a74930dSMiquel Raynal 		if (master->size - slave->offset >= slave->size) {
6862a74930dSMiquel Raynal 			slave->size = master->size - slave->offset
6872a74930dSMiquel Raynal 							- slave->size;
688ff94bc40SHeiko Schocher 		} else {
689ff94bc40SHeiko Schocher 			debug("mtd partition \"%s\" doesn't have enough space: %#llx < %#llx, disabled\n",
690ff94bc40SHeiko Schocher 				part->name, master->size - slave->offset,
6912a74930dSMiquel Raynal 				slave->size);
692ff94bc40SHeiko Schocher 			/* register to preserve ordering */
693ff94bc40SHeiko Schocher 			goto out_register;
694e29c22f5SKyungmin Park 		}
695e29c22f5SKyungmin Park 	}
6962a74930dSMiquel Raynal 	if (slave->size == MTDPART_SIZ_FULL)
6972a74930dSMiquel Raynal 		slave->size = master->size - slave->offset;
698e29c22f5SKyungmin Park 
699ff94bc40SHeiko Schocher 	debug("0x%012llx-0x%012llx : \"%s\"\n", (unsigned long long)slave->offset,
7002a74930dSMiquel Raynal 		(unsigned long long)(slave->offset + slave->size), slave->name);
701e29c22f5SKyungmin Park 
702e29c22f5SKyungmin Park 	/* let's do some sanity checks */
703e29c22f5SKyungmin Park 	if (slave->offset >= master->size) {
704e29c22f5SKyungmin Park 		/* let's register it anyway to preserve ordering */
705e29c22f5SKyungmin Park 		slave->offset = 0;
7062a74930dSMiquel Raynal 		slave->size = 0;
7078d2effeaSStefan Roese 		printk(KERN_ERR"mtd: partition \"%s\" is out of reach -- disabled\n",
7088d2effeaSStefan Roese 			part->name);
7098d2effeaSStefan Roese 		goto out_register;
710e29c22f5SKyungmin Park 	}
7112a74930dSMiquel Raynal 	if (slave->offset + slave->size > master->size) {
7122a74930dSMiquel Raynal 		slave->size = master->size - slave->offset;
7138d2effeaSStefan Roese 		printk(KERN_WARNING"mtd: partition \"%s\" extends beyond the end of device \"%s\" -- size truncated to %#llx\n",
7142a74930dSMiquel Raynal 		       part->name, master->name, slave->size);
715e29c22f5SKyungmin Park 	}
716e29c22f5SKyungmin Park 	if (master->numeraseregions > 1) {
717e29c22f5SKyungmin Park 		/* Deal with variable erase size stuff */
7188d2effeaSStefan Roese 		int i, max = master->numeraseregions;
7192a74930dSMiquel Raynal 		u64 end = slave->offset + slave->size;
720e29c22f5SKyungmin Park 		struct mtd_erase_region_info *regions = master->eraseregions;
721e29c22f5SKyungmin Park 
7228d2effeaSStefan Roese 		/* Find the first erase regions which is part of this
7238d2effeaSStefan Roese 		 * partition. */
7248d2effeaSStefan Roese 		for (i = 0; i < max && regions[i].offset <= slave->offset; i++)
725e29c22f5SKyungmin Park 			;
7268d2effeaSStefan Roese 		/* The loop searched for the region _behind_ the first one */
727ff94bc40SHeiko Schocher 		if (i > 0)
7288d2effeaSStefan Roese 			i--;
729e29c22f5SKyungmin Park 
7308d2effeaSStefan Roese 		/* Pick biggest erasesize */
7318d2effeaSStefan Roese 		for (; i < max && regions[i].offset < end; i++) {
7322a74930dSMiquel Raynal 			if (slave->erasesize < regions[i].erasesize)
7332a74930dSMiquel Raynal 				slave->erasesize = regions[i].erasesize;
734e29c22f5SKyungmin Park 		}
7352a74930dSMiquel Raynal 		WARN_ON(slave->erasesize == 0);
736e29c22f5SKyungmin Park 	} else {
737e29c22f5SKyungmin Park 		/* Single erase size */
7382a74930dSMiquel Raynal 		slave->erasesize = master->erasesize;
739e29c22f5SKyungmin Park 	}
740e29c22f5SKyungmin Park 
7412a74930dSMiquel Raynal 	if ((slave->flags & MTD_WRITEABLE) &&
7422a74930dSMiquel Raynal 	    mtd_mod_by_eb(slave->offset, slave)) {
743e29c22f5SKyungmin Park 		/* Doesn't start on a boundary of major erase size */
7448d2effeaSStefan Roese 		/* FIXME: Let it be writable if it is on a boundary of
7458d2effeaSStefan Roese 		 * _minor_ erase size though */
7462a74930dSMiquel Raynal 		slave->flags &= ~MTD_WRITEABLE;
7478d2effeaSStefan Roese 		printk(KERN_WARNING"mtd: partition \"%s\" doesn't start on an erase block boundary -- force read-only\n",
7488d2effeaSStefan Roese 			part->name);
749e29c22f5SKyungmin Park 	}
7502a74930dSMiquel Raynal 	if ((slave->flags & MTD_WRITEABLE) &&
7512a74930dSMiquel Raynal 	    mtd_mod_by_eb(slave->size, slave)) {
7522a74930dSMiquel Raynal 		slave->flags &= ~MTD_WRITEABLE;
7538d2effeaSStefan Roese 		printk(KERN_WARNING"mtd: partition \"%s\" doesn't end on an erase block -- force read-only\n",
7548d2effeaSStefan Roese 			part->name);
755e29c22f5SKyungmin Park 	}
756e29c22f5SKyungmin Park 
7572a74930dSMiquel Raynal 	slave->ecclayout = master->ecclayout;
7582a74930dSMiquel Raynal 	slave->ecc_step_size = master->ecc_step_size;
7592a74930dSMiquel Raynal 	slave->ecc_strength = master->ecc_strength;
7602a74930dSMiquel Raynal 	slave->bitflip_threshold = master->bitflip_threshold;
761ff94bc40SHeiko Schocher 
762dfe64e2cSSergey Lapin 	if (master->_block_isbad) {
7638d2effeaSStefan Roese 		uint64_t offs = 0;
764e29c22f5SKyungmin Park 
7652a74930dSMiquel Raynal 		while (offs < slave->size) {
766dfe64e2cSSergey Lapin 			if (mtd_block_isbad(master, offs + slave->offset))
7672a74930dSMiquel Raynal 				slave->ecc_stats.badblocks++;
7682a74930dSMiquel Raynal 			offs += slave->erasesize;
769e29c22f5SKyungmin Park 		}
770e29c22f5SKyungmin Park 	}
771e29c22f5SKyungmin Park 
7728d2effeaSStefan Roese out_register:
7738d2effeaSStefan Roese 	return slave;
7748d2effeaSStefan Roese }
7758d2effeaSStefan Roese 
776ddf7bcfaSHeiko Schocher #ifndef __UBOOT__
mtd_add_partition(struct mtd_info * master,const char * name,long long offset,long long length)777ff94bc40SHeiko Schocher int mtd_add_partition(struct mtd_info *master, const char *name,
778ff94bc40SHeiko Schocher 		      long long offset, long long length)
779ff94bc40SHeiko Schocher {
780ff94bc40SHeiko Schocher 	struct mtd_partition part;
7812a74930dSMiquel Raynal 	struct mtd_info *p, *new;
782ff94bc40SHeiko Schocher 	uint64_t start, end;
783ff94bc40SHeiko Schocher 	int ret = 0;
784ff94bc40SHeiko Schocher 
785ff94bc40SHeiko Schocher 	/* the direct offset is expected */
786ff94bc40SHeiko Schocher 	if (offset == MTDPART_OFS_APPEND ||
787ff94bc40SHeiko Schocher 	    offset == MTDPART_OFS_NXTBLK)
788ff94bc40SHeiko Schocher 		return -EINVAL;
789ff94bc40SHeiko Schocher 
790ff94bc40SHeiko Schocher 	if (length == MTDPART_SIZ_FULL)
791ff94bc40SHeiko Schocher 		length = master->size - offset;
792ff94bc40SHeiko Schocher 
793ff94bc40SHeiko Schocher 	if (length <= 0)
794ff94bc40SHeiko Schocher 		return -EINVAL;
795ff94bc40SHeiko Schocher 
796ff94bc40SHeiko Schocher 	part.name = name;
797ff94bc40SHeiko Schocher 	part.size = length;
798ff94bc40SHeiko Schocher 	part.offset = offset;
799ff94bc40SHeiko Schocher 	part.mask_flags = 0;
800ff94bc40SHeiko Schocher 	part.ecclayout = NULL;
801ff94bc40SHeiko Schocher 
802ff94bc40SHeiko Schocher 	new = allocate_partition(master, &part, -1, offset);
803ff94bc40SHeiko Schocher 	if (IS_ERR(new))
804ff94bc40SHeiko Schocher 		return PTR_ERR(new);
805ff94bc40SHeiko Schocher 
806ff94bc40SHeiko Schocher 	start = offset;
807ff94bc40SHeiko Schocher 	end = offset + length;
808ff94bc40SHeiko Schocher 
809ff94bc40SHeiko Schocher 	mutex_lock(&mtd_partitions_mutex);
8102a74930dSMiquel Raynal 	list_for_each_entry(p, &master->partitions, node) {
8112a74930dSMiquel Raynal 		if (start >= p->offset &&
8122a74930dSMiquel Raynal 		    (start < (p->offset + p->size)))
813ff94bc40SHeiko Schocher 			goto err_inv;
814ff94bc40SHeiko Schocher 
8152a74930dSMiquel Raynal 		if (end >= p->offset &&
8162a74930dSMiquel Raynal 		    (end < (p->offset + p->size)))
817ff94bc40SHeiko Schocher 			goto err_inv;
818ff94bc40SHeiko Schocher 	}
819ff94bc40SHeiko Schocher 
8202a74930dSMiquel Raynal 	list_add_tail(&new->node, &master->partitions);
821ff94bc40SHeiko Schocher 	mutex_unlock(&mtd_partitions_mutex);
822ff94bc40SHeiko Schocher 
8232a74930dSMiquel Raynal 	add_mtd_device(new);
824ff94bc40SHeiko Schocher 
825ff94bc40SHeiko Schocher 	return ret;
826ff94bc40SHeiko Schocher err_inv:
827ff94bc40SHeiko Schocher 	mutex_unlock(&mtd_partitions_mutex);
828ff94bc40SHeiko Schocher 	free_partition(new);
829ff94bc40SHeiko Schocher 	return -EINVAL;
830ff94bc40SHeiko Schocher }
831ff94bc40SHeiko Schocher EXPORT_SYMBOL_GPL(mtd_add_partition);
832ff94bc40SHeiko Schocher 
mtd_del_partition(struct mtd_info * master,int partno)833ff94bc40SHeiko Schocher int mtd_del_partition(struct mtd_info *master, int partno)
834ff94bc40SHeiko Schocher {
8352a74930dSMiquel Raynal 	struct mtd_info *slave, *next;
836ff94bc40SHeiko Schocher 	int ret = -EINVAL;
837ff94bc40SHeiko Schocher 
838ff94bc40SHeiko Schocher 	mutex_lock(&mtd_partitions_mutex);
8392a74930dSMiquel Raynal 	list_for_each_entry_safe(slave, next, &master->partitions, node)
8402a74930dSMiquel Raynal 		if (slave->index == partno) {
8412a74930dSMiquel Raynal 			ret = del_mtd_device(slave);
842ff94bc40SHeiko Schocher 			if (ret < 0)
843ff94bc40SHeiko Schocher 				break;
844ff94bc40SHeiko Schocher 
8452a74930dSMiquel Raynal 			list_del(&slave->node);
846ff94bc40SHeiko Schocher 			free_partition(slave);
847ff94bc40SHeiko Schocher 			break;
848ff94bc40SHeiko Schocher 		}
849ff94bc40SHeiko Schocher 	mutex_unlock(&mtd_partitions_mutex);
850ff94bc40SHeiko Schocher 
851ff94bc40SHeiko Schocher 	return ret;
852ff94bc40SHeiko Schocher }
853ff94bc40SHeiko Schocher EXPORT_SYMBOL_GPL(mtd_del_partition);
854ddf7bcfaSHeiko Schocher #endif
855ff94bc40SHeiko Schocher 
8568d2effeaSStefan Roese /*
8578d2effeaSStefan Roese  * This function, given a master MTD object and a partition table, creates
8588d2effeaSStefan Roese  * and registers slave MTD objects which are bound to the master according to
8598d2effeaSStefan Roese  * the partition definitions.
8608d2effeaSStefan Roese  *
8618d2effeaSStefan Roese  * We don't register the master, or expect the caller to have done so,
8628d2effeaSStefan Roese  * for reasons of data integrity.
8638d2effeaSStefan Roese  */
8648d2effeaSStefan Roese 
add_mtd_partitions(struct mtd_info * master,const struct mtd_partition * parts,int nbparts)8658d2effeaSStefan Roese int add_mtd_partitions(struct mtd_info *master,
8668d2effeaSStefan Roese 		       const struct mtd_partition *parts,
8678d2effeaSStefan Roese 		       int nbparts)
8688d2effeaSStefan Roese {
8692a74930dSMiquel Raynal 	struct mtd_info *slave;
8708d2effeaSStefan Roese 	uint64_t cur_offset = 0;
8718d2effeaSStefan Roese 	int i;
8728d2effeaSStefan Roese 
873147162daSJoe Hershberger 	debug("Creating %d MTD partitions on \"%s\":\n", nbparts, master->name);
8748d2effeaSStefan Roese 
8758d2effeaSStefan Roese 	for (i = 0; i < nbparts; i++) {
876ff94bc40SHeiko Schocher 		slave = allocate_partition(master, parts + i, i, cur_offset);
877ff94bc40SHeiko Schocher 		if (IS_ERR(slave))
878ff94bc40SHeiko Schocher 			return PTR_ERR(slave);
879ff94bc40SHeiko Schocher 
880ff94bc40SHeiko Schocher 		mutex_lock(&mtd_partitions_mutex);
8812a74930dSMiquel Raynal 		list_add_tail(&slave->node, &master->partitions);
882ff94bc40SHeiko Schocher 		mutex_unlock(&mtd_partitions_mutex);
883ff94bc40SHeiko Schocher 
8842a74930dSMiquel Raynal 		add_mtd_device(slave);
885ff94bc40SHeiko Schocher 
8862a74930dSMiquel Raynal 		cur_offset = slave->offset + slave->size;
887e29c22f5SKyungmin Park 	}
888e29c22f5SKyungmin Park 
889e29c22f5SKyungmin Park 	return 0;
890e29c22f5SKyungmin Park }
891ff94bc40SHeiko Schocher 
892ff94bc40SHeiko Schocher #ifndef __UBOOT__
893ff94bc40SHeiko Schocher static DEFINE_SPINLOCK(part_parser_lock);
894ff94bc40SHeiko Schocher static LIST_HEAD(part_parsers);
895ff94bc40SHeiko Schocher 
get_partition_parser(const char * name)896ff94bc40SHeiko Schocher static struct mtd_part_parser *get_partition_parser(const char *name)
897ff94bc40SHeiko Schocher {
898ff94bc40SHeiko Schocher 	struct mtd_part_parser *p, *ret = NULL;
899ff94bc40SHeiko Schocher 
900ff94bc40SHeiko Schocher 	spin_lock(&part_parser_lock);
901ff94bc40SHeiko Schocher 
902ff94bc40SHeiko Schocher 	list_for_each_entry(p, &part_parsers, list)
903ff94bc40SHeiko Schocher 		if (!strcmp(p->name, name) && try_module_get(p->owner)) {
904ff94bc40SHeiko Schocher 			ret = p;
905ff94bc40SHeiko Schocher 			break;
906ff94bc40SHeiko Schocher 		}
907ff94bc40SHeiko Schocher 
908ff94bc40SHeiko Schocher 	spin_unlock(&part_parser_lock);
909ff94bc40SHeiko Schocher 
910ff94bc40SHeiko Schocher 	return ret;
911ff94bc40SHeiko Schocher }
912ff94bc40SHeiko Schocher 
913ff94bc40SHeiko Schocher #define put_partition_parser(p) do { module_put((p)->owner); } while (0)
914ff94bc40SHeiko Schocher 
register_mtd_parser(struct mtd_part_parser * p)915ff94bc40SHeiko Schocher void register_mtd_parser(struct mtd_part_parser *p)
916ff94bc40SHeiko Schocher {
917ff94bc40SHeiko Schocher 	spin_lock(&part_parser_lock);
918ff94bc40SHeiko Schocher 	list_add(&p->list, &part_parsers);
919ff94bc40SHeiko Schocher 	spin_unlock(&part_parser_lock);
920ff94bc40SHeiko Schocher }
921ff94bc40SHeiko Schocher EXPORT_SYMBOL_GPL(register_mtd_parser);
922ff94bc40SHeiko Schocher 
deregister_mtd_parser(struct mtd_part_parser * p)923ff94bc40SHeiko Schocher void deregister_mtd_parser(struct mtd_part_parser *p)
924ff94bc40SHeiko Schocher {
925ff94bc40SHeiko Schocher 	spin_lock(&part_parser_lock);
926ff94bc40SHeiko Schocher 	list_del(&p->list);
927ff94bc40SHeiko Schocher 	spin_unlock(&part_parser_lock);
928ff94bc40SHeiko Schocher }
929ff94bc40SHeiko Schocher EXPORT_SYMBOL_GPL(deregister_mtd_parser);
930ff94bc40SHeiko Schocher 
931ff94bc40SHeiko Schocher /*
932ff94bc40SHeiko Schocher  * Do not forget to update 'parse_mtd_partitions()' kerneldoc comment if you
933ff94bc40SHeiko Schocher  * are changing this array!
934ff94bc40SHeiko Schocher  */
935ff94bc40SHeiko Schocher static const char * const default_mtd_part_types[] = {
936ff94bc40SHeiko Schocher 	"cmdlinepart",
937ff94bc40SHeiko Schocher 	"ofpart",
938ff94bc40SHeiko Schocher 	NULL
939ff94bc40SHeiko Schocher };
940ff94bc40SHeiko Schocher 
941ff94bc40SHeiko Schocher /**
942ff94bc40SHeiko Schocher  * parse_mtd_partitions - parse MTD partitions
943ff94bc40SHeiko Schocher  * @master: the master partition (describes whole MTD device)
944ff94bc40SHeiko Schocher  * @types: names of partition parsers to try or %NULL
945ff94bc40SHeiko Schocher  * @pparts: array of partitions found is returned here
946ff94bc40SHeiko Schocher  * @data: MTD partition parser-specific data
947ff94bc40SHeiko Schocher  *
948ff94bc40SHeiko Schocher  * This function tries to find partition on MTD device @master. It uses MTD
949ff94bc40SHeiko Schocher  * partition parsers, specified in @types. However, if @types is %NULL, then
950ff94bc40SHeiko Schocher  * the default list of parsers is used. The default list contains only the
951ff94bc40SHeiko Schocher  * "cmdlinepart" and "ofpart" parsers ATM.
952ff94bc40SHeiko Schocher  * Note: If there are more then one parser in @types, the kernel only takes the
953ff94bc40SHeiko Schocher  * partitions parsed out by the first parser.
954ff94bc40SHeiko Schocher  *
955ff94bc40SHeiko Schocher  * This function may return:
956ff94bc40SHeiko Schocher  * o a negative error code in case of failure
957ff94bc40SHeiko Schocher  * o zero if no partitions were found
958ff94bc40SHeiko Schocher  * o a positive number of found partitions, in which case on exit @pparts will
959ff94bc40SHeiko Schocher  *   point to an array containing this number of &struct mtd_info objects.
960ff94bc40SHeiko Schocher  */
parse_mtd_partitions(struct mtd_info * master,const char * const * types,struct mtd_partition ** pparts,struct mtd_part_parser_data * data)961ff94bc40SHeiko Schocher int parse_mtd_partitions(struct mtd_info *master, const char *const *types,
962ff94bc40SHeiko Schocher 			 struct mtd_partition **pparts,
963ff94bc40SHeiko Schocher 			 struct mtd_part_parser_data *data)
964ff94bc40SHeiko Schocher {
965ff94bc40SHeiko Schocher 	struct mtd_part_parser *parser;
966ff94bc40SHeiko Schocher 	int ret = 0;
967ff94bc40SHeiko Schocher 
968ff94bc40SHeiko Schocher 	if (!types)
969ff94bc40SHeiko Schocher 		types = default_mtd_part_types;
970ff94bc40SHeiko Schocher 
971ff94bc40SHeiko Schocher 	for ( ; ret <= 0 && *types; types++) {
972ff94bc40SHeiko Schocher 		parser = get_partition_parser(*types);
973ff94bc40SHeiko Schocher 		if (!parser && !request_module("%s", *types))
974ff94bc40SHeiko Schocher 			parser = get_partition_parser(*types);
975ff94bc40SHeiko Schocher 		if (!parser)
976ff94bc40SHeiko Schocher 			continue;
977ff94bc40SHeiko Schocher 		ret = (*parser->parse_fn)(master, pparts, data);
978ff94bc40SHeiko Schocher 		put_partition_parser(parser);
979ff94bc40SHeiko Schocher 		if (ret > 0) {
980ff94bc40SHeiko Schocher 			printk(KERN_NOTICE "%d %s partitions found on MTD device %s\n",
981ff94bc40SHeiko Schocher 			       ret, parser->name, master->name);
982ff94bc40SHeiko Schocher 			break;
983ff94bc40SHeiko Schocher 		}
984ff94bc40SHeiko Schocher 	}
985ff94bc40SHeiko Schocher 	return ret;
986ff94bc40SHeiko Schocher }
987ff94bc40SHeiko Schocher #endif
988ff94bc40SHeiko Schocher 
989ff94bc40SHeiko Schocher /* Returns the size of the entire flash chip */
mtd_get_device_size(const struct mtd_info * mtd)990ff94bc40SHeiko Schocher uint64_t mtd_get_device_size(const struct mtd_info *mtd)
991ff94bc40SHeiko Schocher {
9922a74930dSMiquel Raynal 	if (mtd_is_partition(mtd))
9932a74930dSMiquel Raynal 		return mtd->parent->size;
994ff94bc40SHeiko Schocher 
9952a74930dSMiquel Raynal 	return mtd->size;
996ff94bc40SHeiko Schocher }
997ff94bc40SHeiko Schocher EXPORT_SYMBOL_GPL(mtd_get_device_size);
998