xref: /rk3399_rockchip-uboot/drivers/mtd/mtdpart.c (revision 4e67c57125290b25467134638cefdcc74c6eebeb)
1e29c22f5SKyungmin Park /*
2e29c22f5SKyungmin Park  * Simple MTD partitioning layer
3e29c22f5SKyungmin Park  *
4ff94bc40SHeiko Schocher  * Copyright © 2000 Nicolas Pitre <nico@fluxnic.net>
5ff94bc40SHeiko Schocher  * Copyright © 2002 Thomas Gleixner <gleixner@linutronix.de>
6ff94bc40SHeiko Schocher  * Copyright © 2000-2010 David Woodhouse <dwmw2@infradead.org>
7e29c22f5SKyungmin Park  *
8ff94bc40SHeiko Schocher  * SPDX-License-Identifier:	GPL-2.0+
9e29c22f5SKyungmin Park  *
10e29c22f5SKyungmin Park  */
11e29c22f5SKyungmin Park 
12ff94bc40SHeiko Schocher #define __UBOOT__
13ff94bc40SHeiko Schocher #ifndef __UBOOT__
14ff94bc40SHeiko Schocher #include <linux/module.h>
15ff94bc40SHeiko Schocher #include <linux/types.h>
16ff94bc40SHeiko Schocher #include <linux/kernel.h>
17ff94bc40SHeiko Schocher #include <linux/slab.h>
18ff94bc40SHeiko Schocher #include <linux/list.h>
19ff94bc40SHeiko Schocher #include <linux/kmod.h>
20ff94bc40SHeiko Schocher #endif
21ff94bc40SHeiko Schocher 
22e29c22f5SKyungmin Park #include <common.h>
23e29c22f5SKyungmin Park #include <malloc.h>
24e29c22f5SKyungmin Park #include <asm/errno.h>
25ff94bc40SHeiko Schocher #include <linux/compat.h>
26ff94bc40SHeiko Schocher #include <ubi_uboot.h>
27e29c22f5SKyungmin Park 
28e29c22f5SKyungmin Park #include <linux/mtd/mtd.h>
29e29c22f5SKyungmin Park #include <linux/mtd/partitions.h>
30ff94bc40SHeiko Schocher #include <linux/err.h>
31ff94bc40SHeiko Schocher 
32ff94bc40SHeiko Schocher #include "mtdcore.h"
33e29c22f5SKyungmin Park 
34e29c22f5SKyungmin Park /* Our partition linked list */
35ff94bc40SHeiko Schocher static LIST_HEAD(mtd_partitions);
36ff94bc40SHeiko Schocher #ifndef __UBOOT__
37ff94bc40SHeiko Schocher static DEFINE_MUTEX(mtd_partitions_mutex);
38ff94bc40SHeiko Schocher #else
39ff94bc40SHeiko Schocher DEFINE_MUTEX(mtd_partitions_mutex);
40ff94bc40SHeiko Schocher #endif
41e29c22f5SKyungmin Park 
42e29c22f5SKyungmin Park /* Our partition node structure */
43e29c22f5SKyungmin Park struct mtd_part {
44e29c22f5SKyungmin Park 	struct mtd_info mtd;
45e29c22f5SKyungmin Park 	struct mtd_info *master;
468d2effeaSStefan Roese 	uint64_t offset;
47e29c22f5SKyungmin Park 	struct list_head list;
48e29c22f5SKyungmin Park };
49e29c22f5SKyungmin Park 
50e29c22f5SKyungmin Park /*
51e29c22f5SKyungmin Park  * Given a pointer to the MTD object in the mtd_part structure, we can retrieve
52e29c22f5SKyungmin Park  * the pointer to that structure with this macro.
53e29c22f5SKyungmin Park  */
54e29c22f5SKyungmin Park #define PART(x)  ((struct mtd_part *)(x))
55e29c22f5SKyungmin Park 
56e29c22f5SKyungmin Park 
57ff94bc40SHeiko Schocher #ifdef __UBOOT__
58ff94bc40SHeiko Schocher /* from mm/util.c */
59ff94bc40SHeiko Schocher 
60ff94bc40SHeiko Schocher /**
61ff94bc40SHeiko Schocher  * kstrdup - allocate space for and copy an existing string
62ff94bc40SHeiko Schocher  * @s: the string to duplicate
63ff94bc40SHeiko Schocher  * @gfp: the GFP mask used in the kmalloc() call when allocating memory
64ff94bc40SHeiko Schocher  */
65ff94bc40SHeiko Schocher char *kstrdup(const char *s, gfp_t gfp)
66ff94bc40SHeiko Schocher {
67ff94bc40SHeiko Schocher 	size_t len;
68ff94bc40SHeiko Schocher 	char *buf;
69ff94bc40SHeiko Schocher 
70ff94bc40SHeiko Schocher 	if (!s)
71ff94bc40SHeiko Schocher 		return NULL;
72ff94bc40SHeiko Schocher 
73ff94bc40SHeiko Schocher 	len = strlen(s) + 1;
74ff94bc40SHeiko Schocher 	buf = kmalloc(len, gfp);
75ff94bc40SHeiko Schocher 	if (buf)
76ff94bc40SHeiko Schocher 		memcpy(buf, s, len);
77ff94bc40SHeiko Schocher 	return buf;
78ff94bc40SHeiko Schocher }
79ff94bc40SHeiko Schocher #endif
80ff94bc40SHeiko Schocher 
81e29c22f5SKyungmin Park /*
82e29c22f5SKyungmin Park  * MTD methods which simply translate the effective address and pass through
83e29c22f5SKyungmin Park  * to the _real_ device.
84e29c22f5SKyungmin Park  */
85e29c22f5SKyungmin Park 
86e29c22f5SKyungmin Park static int part_read(struct mtd_info *mtd, loff_t from, size_t len,
87e29c22f5SKyungmin Park 		size_t *retlen, u_char *buf)
88e29c22f5SKyungmin Park {
89e29c22f5SKyungmin Park 	struct mtd_part *part = PART(mtd);
908d2effeaSStefan Roese 	struct mtd_ecc_stats stats;
91e29c22f5SKyungmin Park 	int res;
92e29c22f5SKyungmin Park 
938d2effeaSStefan Roese 	stats = part->master->ecc_stats;
94ff94bc40SHeiko Schocher 	res = part->master->_read(part->master, from + part->offset, len,
95ff94bc40SHeiko Schocher 				  retlen, buf);
9640462e54SPaul Burton 	if (unlikely(mtd_is_eccerr(res)))
9740462e54SPaul Burton 		mtd->ecc_stats.failed +=
9840462e54SPaul Burton 			part->master->ecc_stats.failed - stats.failed;
9940462e54SPaul Burton 	else
10040462e54SPaul Burton 		mtd->ecc_stats.corrected +=
10140462e54SPaul Burton 			part->master->ecc_stats.corrected - stats.corrected;
102e29c22f5SKyungmin Park 	return res;
103e29c22f5SKyungmin Park }
104e29c22f5SKyungmin Park 
105ff94bc40SHeiko Schocher #ifndef __UBOOT__
106ff94bc40SHeiko Schocher static int part_point(struct mtd_info *mtd, loff_t from, size_t len,
107ff94bc40SHeiko Schocher 		size_t *retlen, void **virt, resource_size_t *phys)
108ff94bc40SHeiko Schocher {
109ff94bc40SHeiko Schocher 	struct mtd_part *part = PART(mtd);
110ff94bc40SHeiko Schocher 
111ff94bc40SHeiko Schocher 	return part->master->_point(part->master, from + part->offset, len,
112ff94bc40SHeiko Schocher 				    retlen, virt, phys);
113ff94bc40SHeiko Schocher }
114ff94bc40SHeiko Schocher 
115ff94bc40SHeiko Schocher static int part_unpoint(struct mtd_info *mtd, loff_t from, size_t len)
116ff94bc40SHeiko Schocher {
117ff94bc40SHeiko Schocher 	struct mtd_part *part = PART(mtd);
118ff94bc40SHeiko Schocher 
119ff94bc40SHeiko Schocher 	return part->master->_unpoint(part->master, from + part->offset, len);
120ff94bc40SHeiko Schocher }
121ff94bc40SHeiko Schocher #endif
122ff94bc40SHeiko Schocher 
123ff94bc40SHeiko Schocher static unsigned long part_get_unmapped_area(struct mtd_info *mtd,
124ff94bc40SHeiko Schocher 					    unsigned long len,
125ff94bc40SHeiko Schocher 					    unsigned long offset,
126ff94bc40SHeiko Schocher 					    unsigned long flags)
127ff94bc40SHeiko Schocher {
128ff94bc40SHeiko Schocher 	struct mtd_part *part = PART(mtd);
129ff94bc40SHeiko Schocher 
130ff94bc40SHeiko Schocher 	offset += part->offset;
131ff94bc40SHeiko Schocher 	return part->master->_get_unmapped_area(part->master, len, offset,
132ff94bc40SHeiko Schocher 						flags);
133ff94bc40SHeiko Schocher }
134ff94bc40SHeiko Schocher 
135e29c22f5SKyungmin Park static int part_read_oob(struct mtd_info *mtd, loff_t from,
136e29c22f5SKyungmin Park 		struct mtd_oob_ops *ops)
137e29c22f5SKyungmin Park {
138e29c22f5SKyungmin Park 	struct mtd_part *part = PART(mtd);
139e29c22f5SKyungmin Park 	int res;
140e29c22f5SKyungmin Park 
141e29c22f5SKyungmin Park 	if (from >= mtd->size)
142e29c22f5SKyungmin Park 		return -EINVAL;
143e29c22f5SKyungmin Park 	if (ops->datbuf && from + ops->len > mtd->size)
144e29c22f5SKyungmin Park 		return -EINVAL;
145e29c22f5SKyungmin Park 
146ff94bc40SHeiko Schocher 	/*
147ff94bc40SHeiko Schocher 	 * If OOB is also requested, make sure that we do not read past the end
148ff94bc40SHeiko Schocher 	 * of this partition.
149ff94bc40SHeiko Schocher 	 */
150ff94bc40SHeiko Schocher 	if (ops->oobbuf) {
151ff94bc40SHeiko Schocher 		size_t len, pages;
152ff94bc40SHeiko Schocher 
153ff94bc40SHeiko Schocher 		if (ops->mode == MTD_OPS_AUTO_OOB)
154ff94bc40SHeiko Schocher 			len = mtd->oobavail;
155ff94bc40SHeiko Schocher 		else
156ff94bc40SHeiko Schocher 			len = mtd->oobsize;
157ff94bc40SHeiko Schocher 		pages = mtd_div_by_ws(mtd->size, mtd);
158ff94bc40SHeiko Schocher 		pages -= mtd_div_by_ws(from, mtd);
159ff94bc40SHeiko Schocher 		if (ops->ooboffs + ops->ooblen > pages * len)
160ff94bc40SHeiko Schocher 			return -EINVAL;
161ff94bc40SHeiko Schocher 	}
162ff94bc40SHeiko Schocher 
163ff94bc40SHeiko Schocher 	res = part->master->_read_oob(part->master, from + part->offset, ops);
164e29c22f5SKyungmin Park 	if (unlikely(res)) {
165dfe64e2cSSergey Lapin 		if (mtd_is_bitflip(res))
166e29c22f5SKyungmin Park 			mtd->ecc_stats.corrected++;
167dfe64e2cSSergey Lapin 		if (mtd_is_eccerr(res))
168e29c22f5SKyungmin Park 			mtd->ecc_stats.failed++;
169e29c22f5SKyungmin Park 	}
170e29c22f5SKyungmin Park 	return res;
171e29c22f5SKyungmin Park }
172e29c22f5SKyungmin Park 
1738d2effeaSStefan Roese static int part_read_user_prot_reg(struct mtd_info *mtd, loff_t from,
1748d2effeaSStefan Roese 		size_t len, size_t *retlen, u_char *buf)
175e29c22f5SKyungmin Park {
176e29c22f5SKyungmin Park 	struct mtd_part *part = PART(mtd);
177ff94bc40SHeiko Schocher 	return part->master->_read_user_prot_reg(part->master, from, len,
178ff94bc40SHeiko Schocher 						 retlen, buf);
179e29c22f5SKyungmin Park }
180e29c22f5SKyungmin Park 
181*4e67c571SHeiko Schocher static int part_get_user_prot_info(struct mtd_info *mtd, size_t len,
182*4e67c571SHeiko Schocher 				   size_t *retlen, struct otp_info *buf)
183e29c22f5SKyungmin Park {
184e29c22f5SKyungmin Park 	struct mtd_part *part = PART(mtd);
185*4e67c571SHeiko Schocher 	return part->master->_get_user_prot_info(part->master, len, retlen,
186*4e67c571SHeiko Schocher 						 buf);
187e29c22f5SKyungmin Park }
188e29c22f5SKyungmin Park 
1898d2effeaSStefan Roese static int part_read_fact_prot_reg(struct mtd_info *mtd, loff_t from,
1908d2effeaSStefan Roese 		size_t len, size_t *retlen, u_char *buf)
191e29c22f5SKyungmin Park {
192e29c22f5SKyungmin Park 	struct mtd_part *part = PART(mtd);
193ff94bc40SHeiko Schocher 	return part->master->_read_fact_prot_reg(part->master, from, len,
194ff94bc40SHeiko Schocher 						 retlen, buf);
195e29c22f5SKyungmin Park }
196e29c22f5SKyungmin Park 
197*4e67c571SHeiko Schocher static int part_get_fact_prot_info(struct mtd_info *mtd, size_t len,
198*4e67c571SHeiko Schocher 				   size_t *retlen, struct otp_info *buf)
199e29c22f5SKyungmin Park {
200e29c22f5SKyungmin Park 	struct mtd_part *part = PART(mtd);
201*4e67c571SHeiko Schocher 	return part->master->_get_fact_prot_info(part->master, len, retlen,
202*4e67c571SHeiko Schocher 						 buf);
203e29c22f5SKyungmin Park }
204e29c22f5SKyungmin Park 
205e29c22f5SKyungmin Park static int part_write(struct mtd_info *mtd, loff_t to, size_t len,
206e29c22f5SKyungmin Park 		size_t *retlen, const u_char *buf)
207e29c22f5SKyungmin Park {
208e29c22f5SKyungmin Park 	struct mtd_part *part = PART(mtd);
209ff94bc40SHeiko Schocher 	return part->master->_write(part->master, to + part->offset, len,
210ff94bc40SHeiko Schocher 				    retlen, buf);
211ff94bc40SHeiko Schocher }
212ff94bc40SHeiko Schocher 
213ff94bc40SHeiko Schocher static int part_panic_write(struct mtd_info *mtd, loff_t to, size_t len,
214ff94bc40SHeiko Schocher 		size_t *retlen, const u_char *buf)
215ff94bc40SHeiko Schocher {
216ff94bc40SHeiko Schocher 	struct mtd_part *part = PART(mtd);
217ff94bc40SHeiko Schocher 	return part->master->_panic_write(part->master, to + part->offset, len,
218ff94bc40SHeiko Schocher 					  retlen, buf);
219e29c22f5SKyungmin Park }
220e29c22f5SKyungmin Park 
221e29c22f5SKyungmin Park static int part_write_oob(struct mtd_info *mtd, loff_t to,
222e29c22f5SKyungmin Park 		struct mtd_oob_ops *ops)
223e29c22f5SKyungmin Park {
224e29c22f5SKyungmin Park 	struct mtd_part *part = PART(mtd);
225e29c22f5SKyungmin Park 
226e29c22f5SKyungmin Park 	if (to >= mtd->size)
227e29c22f5SKyungmin Park 		return -EINVAL;
228e29c22f5SKyungmin Park 	if (ops->datbuf && to + ops->len > mtd->size)
229e29c22f5SKyungmin Park 		return -EINVAL;
230ff94bc40SHeiko Schocher 	return part->master->_write_oob(part->master, to + part->offset, ops);
231e29c22f5SKyungmin Park }
232e29c22f5SKyungmin Park 
2338d2effeaSStefan Roese static int part_write_user_prot_reg(struct mtd_info *mtd, loff_t from,
2348d2effeaSStefan Roese 		size_t len, size_t *retlen, u_char *buf)
235e29c22f5SKyungmin Park {
236e29c22f5SKyungmin Park 	struct mtd_part *part = PART(mtd);
237ff94bc40SHeiko Schocher 	return part->master->_write_user_prot_reg(part->master, from, len,
238ff94bc40SHeiko Schocher 						  retlen, buf);
239e29c22f5SKyungmin Park }
240e29c22f5SKyungmin Park 
2418d2effeaSStefan Roese static int part_lock_user_prot_reg(struct mtd_info *mtd, loff_t from,
2428d2effeaSStefan Roese 		size_t len)
243e29c22f5SKyungmin Park {
244e29c22f5SKyungmin Park 	struct mtd_part *part = PART(mtd);
245ff94bc40SHeiko Schocher 	return part->master->_lock_user_prot_reg(part->master, from, len);
246e29c22f5SKyungmin Park }
247e29c22f5SKyungmin Park 
248ff94bc40SHeiko Schocher #ifndef __UBOOT__
249ff94bc40SHeiko Schocher static int part_writev(struct mtd_info *mtd, const struct kvec *vecs,
250ff94bc40SHeiko Schocher 		unsigned long count, loff_t to, size_t *retlen)
251ff94bc40SHeiko Schocher {
252ff94bc40SHeiko Schocher 	struct mtd_part *part = PART(mtd);
253ff94bc40SHeiko Schocher 	return part->master->_writev(part->master, vecs, count,
254ff94bc40SHeiko Schocher 				     to + part->offset, retlen);
255ff94bc40SHeiko Schocher }
256ff94bc40SHeiko Schocher #endif
257ff94bc40SHeiko Schocher 
258e29c22f5SKyungmin Park static int part_erase(struct mtd_info *mtd, struct erase_info *instr)
259e29c22f5SKyungmin Park {
260e29c22f5SKyungmin Park 	struct mtd_part *part = PART(mtd);
261e29c22f5SKyungmin Park 	int ret;
262dfe64e2cSSergey Lapin 
263e29c22f5SKyungmin Park 	instr->addr += part->offset;
264ff94bc40SHeiko Schocher 	ret = part->master->_erase(part->master, instr);
265e29c22f5SKyungmin Park 	if (ret) {
2668d2effeaSStefan Roese 		if (instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN)
267e29c22f5SKyungmin Park 			instr->fail_addr -= part->offset;
268e29c22f5SKyungmin Park 		instr->addr -= part->offset;
269e29c22f5SKyungmin Park 	}
270e29c22f5SKyungmin Park 	return ret;
271e29c22f5SKyungmin Park }
272e29c22f5SKyungmin Park 
273e29c22f5SKyungmin Park void mtd_erase_callback(struct erase_info *instr)
274e29c22f5SKyungmin Park {
275dfe64e2cSSergey Lapin 	if (instr->mtd->_erase == part_erase) {
276e29c22f5SKyungmin Park 		struct mtd_part *part = PART(instr->mtd);
277e29c22f5SKyungmin Park 
2788d2effeaSStefan Roese 		if (instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN)
279e29c22f5SKyungmin Park 			instr->fail_addr -= part->offset;
280e29c22f5SKyungmin Park 		instr->addr -= part->offset;
281e29c22f5SKyungmin Park 	}
282e29c22f5SKyungmin Park 	if (instr->callback)
283e29c22f5SKyungmin Park 		instr->callback(instr);
284e29c22f5SKyungmin Park }
285ff94bc40SHeiko Schocher EXPORT_SYMBOL_GPL(mtd_erase_callback);
286e29c22f5SKyungmin Park 
2878d2effeaSStefan Roese static int part_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
288e29c22f5SKyungmin Park {
289e29c22f5SKyungmin Park 	struct mtd_part *part = PART(mtd);
290ff94bc40SHeiko Schocher 	return part->master->_lock(part->master, ofs + part->offset, len);
291e29c22f5SKyungmin Park }
292e29c22f5SKyungmin Park 
2938d2effeaSStefan Roese static int part_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
294e29c22f5SKyungmin Park {
295e29c22f5SKyungmin Park 	struct mtd_part *part = PART(mtd);
296ff94bc40SHeiko Schocher 	return part->master->_unlock(part->master, ofs + part->offset, len);
297ff94bc40SHeiko Schocher }
298ff94bc40SHeiko Schocher 
299ff94bc40SHeiko Schocher static int part_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len)
300ff94bc40SHeiko Schocher {
301ff94bc40SHeiko Schocher 	struct mtd_part *part = PART(mtd);
302ff94bc40SHeiko Schocher 	return part->master->_is_locked(part->master, ofs + part->offset, len);
303e29c22f5SKyungmin Park }
304e29c22f5SKyungmin Park 
305e29c22f5SKyungmin Park static void part_sync(struct mtd_info *mtd)
306e29c22f5SKyungmin Park {
307e29c22f5SKyungmin Park 	struct mtd_part *part = PART(mtd);
308ff94bc40SHeiko Schocher 	part->master->_sync(part->master);
309e29c22f5SKyungmin Park }
310e29c22f5SKyungmin Park 
311ff94bc40SHeiko Schocher #ifndef __UBOOT__
312ff94bc40SHeiko Schocher static int part_suspend(struct mtd_info *mtd)
313ff94bc40SHeiko Schocher {
314ff94bc40SHeiko Schocher 	struct mtd_part *part = PART(mtd);
315ff94bc40SHeiko Schocher 	return part->master->_suspend(part->master);
316ff94bc40SHeiko Schocher }
317ff94bc40SHeiko Schocher 
318ff94bc40SHeiko Schocher static void part_resume(struct mtd_info *mtd)
319ff94bc40SHeiko Schocher {
320ff94bc40SHeiko Schocher 	struct mtd_part *part = PART(mtd);
321ff94bc40SHeiko Schocher 	part->master->_resume(part->master);
322ff94bc40SHeiko Schocher }
323ff94bc40SHeiko Schocher #endif
324ff94bc40SHeiko Schocher 
325e29c22f5SKyungmin Park static int part_block_isbad(struct mtd_info *mtd, loff_t ofs)
326e29c22f5SKyungmin Park {
327e29c22f5SKyungmin Park 	struct mtd_part *part = PART(mtd);
328e29c22f5SKyungmin Park 	ofs += part->offset;
329ff94bc40SHeiko Schocher 	return part->master->_block_isbad(part->master, ofs);
330e29c22f5SKyungmin Park }
331e29c22f5SKyungmin Park 
332e29c22f5SKyungmin Park static int part_block_markbad(struct mtd_info *mtd, loff_t ofs)
333e29c22f5SKyungmin Park {
334e29c22f5SKyungmin Park 	struct mtd_part *part = PART(mtd);
335e29c22f5SKyungmin Park 	int res;
336e29c22f5SKyungmin Park 
337e29c22f5SKyungmin Park 	ofs += part->offset;
338ff94bc40SHeiko Schocher 	res = part->master->_block_markbad(part->master, ofs);
339e29c22f5SKyungmin Park 	if (!res)
340e29c22f5SKyungmin Park 		mtd->ecc_stats.badblocks++;
341e29c22f5SKyungmin Park 	return res;
342e29c22f5SKyungmin Park }
343e29c22f5SKyungmin Park 
344ff94bc40SHeiko Schocher static inline void free_partition(struct mtd_part *p)
345ff94bc40SHeiko Schocher {
346ff94bc40SHeiko Schocher 	kfree(p->mtd.name);
347ff94bc40SHeiko Schocher 	kfree(p);
348ff94bc40SHeiko Schocher }
349ff94bc40SHeiko Schocher 
350e29c22f5SKyungmin Park /*
351e29c22f5SKyungmin Park  * This function unregisters and destroy all slave MTD objects which are
352e29c22f5SKyungmin Park  * attached to the given master MTD object.
353e29c22f5SKyungmin Park  */
354e29c22f5SKyungmin Park 
355e29c22f5SKyungmin Park int del_mtd_partitions(struct mtd_info *master)
356e29c22f5SKyungmin Park {
3578d2effeaSStefan Roese 	struct mtd_part *slave, *next;
358ff94bc40SHeiko Schocher 	int ret, err = 0;
359e29c22f5SKyungmin Park 
360ff94bc40SHeiko Schocher 	mutex_lock(&mtd_partitions_mutex);
3618d2effeaSStefan Roese 	list_for_each_entry_safe(slave, next, &mtd_partitions, list)
362e29c22f5SKyungmin Park 		if (slave->master == master) {
363ff94bc40SHeiko Schocher 			ret = del_mtd_device(&slave->mtd);
364ff94bc40SHeiko Schocher 			if (ret < 0) {
365ff94bc40SHeiko Schocher 				err = ret;
366ff94bc40SHeiko Schocher 				continue;
367ff94bc40SHeiko Schocher 			}
3688d2effeaSStefan Roese 			list_del(&slave->list);
369ff94bc40SHeiko Schocher 			free_partition(slave);
370ff94bc40SHeiko Schocher 		}
371ff94bc40SHeiko Schocher 	mutex_unlock(&mtd_partitions_mutex);
372ff94bc40SHeiko Schocher 
373ff94bc40SHeiko Schocher 	return err;
374e29c22f5SKyungmin Park }
375e29c22f5SKyungmin Park 
376ff94bc40SHeiko Schocher static struct mtd_part *allocate_partition(struct mtd_info *master,
3778d2effeaSStefan Roese 			const struct mtd_partition *part, int partno,
3788d2effeaSStefan Roese 			uint64_t cur_offset)
379e29c22f5SKyungmin Park {
380e29c22f5SKyungmin Park 	struct mtd_part *slave;
381ff94bc40SHeiko Schocher 	char *name;
382e29c22f5SKyungmin Park 
383e29c22f5SKyungmin Park 	/* allocate the partition structure */
384e29c22f5SKyungmin Park 	slave = kzalloc(sizeof(*slave), GFP_KERNEL);
385ff94bc40SHeiko Schocher 	name = kstrdup(part->name, GFP_KERNEL);
386ff94bc40SHeiko Schocher 	if (!name || !slave) {
3878d2effeaSStefan Roese 		printk(KERN_ERR"memory allocation error while creating partitions for \"%s\"\n",
388e29c22f5SKyungmin Park 		       master->name);
389ff94bc40SHeiko Schocher 		kfree(name);
390ff94bc40SHeiko Schocher 		kfree(slave);
391ff94bc40SHeiko Schocher 		return ERR_PTR(-ENOMEM);
392e29c22f5SKyungmin Park 	}
393e29c22f5SKyungmin Park 
394e29c22f5SKyungmin Park 	/* set up the MTD object for this partition */
395e29c22f5SKyungmin Park 	slave->mtd.type = master->type;
3968d2effeaSStefan Roese 	slave->mtd.flags = master->flags & ~part->mask_flags;
3978d2effeaSStefan Roese 	slave->mtd.size = part->size;
398e29c22f5SKyungmin Park 	slave->mtd.writesize = master->writesize;
399ff94bc40SHeiko Schocher 	slave->mtd.writebufsize = master->writebufsize;
400e29c22f5SKyungmin Park 	slave->mtd.oobsize = master->oobsize;
401e29c22f5SKyungmin Park 	slave->mtd.oobavail = master->oobavail;
402e29c22f5SKyungmin Park 	slave->mtd.subpage_sft = master->subpage_sft;
403e29c22f5SKyungmin Park 
404ff94bc40SHeiko Schocher 	slave->mtd.name = name;
405e29c22f5SKyungmin Park 	slave->mtd.owner = master->owner;
406ff94bc40SHeiko Schocher #ifndef __UBOOT__
407ff94bc40SHeiko Schocher 	slave->mtd.backing_dev_info = master->backing_dev_info;
408ff94bc40SHeiko Schocher 
409ff94bc40SHeiko Schocher 	/* NOTE:  we don't arrange MTDs as a tree; it'd be error-prone
410ff94bc40SHeiko Schocher 	 * to have the same data be in two different partitions.
411ff94bc40SHeiko Schocher 	 */
412ff94bc40SHeiko Schocher 	slave->mtd.dev.parent = master->dev.parent;
413ff94bc40SHeiko Schocher #endif
414e29c22f5SKyungmin Park 
415dfe64e2cSSergey Lapin 	slave->mtd._read = part_read;
416dfe64e2cSSergey Lapin 	slave->mtd._write = part_write;
417e29c22f5SKyungmin Park 
418ff94bc40SHeiko Schocher 	if (master->_panic_write)
419ff94bc40SHeiko Schocher 		slave->mtd._panic_write = part_panic_write;
420ff94bc40SHeiko Schocher 
421ff94bc40SHeiko Schocher #ifndef __UBOOT__
422ff94bc40SHeiko Schocher 	if (master->_point && master->_unpoint) {
423ff94bc40SHeiko Schocher 		slave->mtd._point = part_point;
424ff94bc40SHeiko Schocher 		slave->mtd._unpoint = part_unpoint;
425ff94bc40SHeiko Schocher 	}
426ff94bc40SHeiko Schocher #endif
427ff94bc40SHeiko Schocher 
428ff94bc40SHeiko Schocher 	if (master->_get_unmapped_area)
429ff94bc40SHeiko Schocher 		slave->mtd._get_unmapped_area = part_get_unmapped_area;
430dfe64e2cSSergey Lapin 	if (master->_read_oob)
431dfe64e2cSSergey Lapin 		slave->mtd._read_oob = part_read_oob;
432dfe64e2cSSergey Lapin 	if (master->_write_oob)
433dfe64e2cSSergey Lapin 		slave->mtd._write_oob = part_write_oob;
434dfe64e2cSSergey Lapin 	if (master->_read_user_prot_reg)
435dfe64e2cSSergey Lapin 		slave->mtd._read_user_prot_reg = part_read_user_prot_reg;
436dfe64e2cSSergey Lapin 	if (master->_read_fact_prot_reg)
437dfe64e2cSSergey Lapin 		slave->mtd._read_fact_prot_reg = part_read_fact_prot_reg;
438dfe64e2cSSergey Lapin 	if (master->_write_user_prot_reg)
439dfe64e2cSSergey Lapin 		slave->mtd._write_user_prot_reg = part_write_user_prot_reg;
440dfe64e2cSSergey Lapin 	if (master->_lock_user_prot_reg)
441dfe64e2cSSergey Lapin 		slave->mtd._lock_user_prot_reg = part_lock_user_prot_reg;
442dfe64e2cSSergey Lapin 	if (master->_get_user_prot_info)
443dfe64e2cSSergey Lapin 		slave->mtd._get_user_prot_info = part_get_user_prot_info;
444dfe64e2cSSergey Lapin 	if (master->_get_fact_prot_info)
445dfe64e2cSSergey Lapin 		slave->mtd._get_fact_prot_info = part_get_fact_prot_info;
446dfe64e2cSSergey Lapin 	if (master->_sync)
447dfe64e2cSSergey Lapin 		slave->mtd._sync = part_sync;
448ff94bc40SHeiko Schocher #ifndef __UBOOT__
449ff94bc40SHeiko Schocher 	if (!partno && !master->dev.class && master->_suspend &&
450ff94bc40SHeiko Schocher 	    master->_resume) {
451ff94bc40SHeiko Schocher 			slave->mtd._suspend = part_suspend;
452ff94bc40SHeiko Schocher 			slave->mtd._resume = part_resume;
453ff94bc40SHeiko Schocher 	}
454ff94bc40SHeiko Schocher 	if (master->_writev)
455ff94bc40SHeiko Schocher 		slave->mtd._writev = part_writev;
456ff94bc40SHeiko Schocher #endif
457dfe64e2cSSergey Lapin 	if (master->_lock)
458dfe64e2cSSergey Lapin 		slave->mtd._lock = part_lock;
459dfe64e2cSSergey Lapin 	if (master->_unlock)
460dfe64e2cSSergey Lapin 		slave->mtd._unlock = part_unlock;
461ff94bc40SHeiko Schocher 	if (master->_is_locked)
462ff94bc40SHeiko Schocher 		slave->mtd._is_locked = part_is_locked;
463dfe64e2cSSergey Lapin 	if (master->_block_isbad)
464dfe64e2cSSergey Lapin 		slave->mtd._block_isbad = part_block_isbad;
465dfe64e2cSSergey Lapin 	if (master->_block_markbad)
466dfe64e2cSSergey Lapin 		slave->mtd._block_markbad = part_block_markbad;
467dfe64e2cSSergey Lapin 	slave->mtd._erase = part_erase;
468e29c22f5SKyungmin Park 	slave->master = master;
4698d2effeaSStefan Roese 	slave->offset = part->offset;
470e29c22f5SKyungmin Park 
471e29c22f5SKyungmin Park 	if (slave->offset == MTDPART_OFS_APPEND)
472e29c22f5SKyungmin Park 		slave->offset = cur_offset;
473e29c22f5SKyungmin Park 	if (slave->offset == MTDPART_OFS_NXTBLK) {
474e29c22f5SKyungmin Park 		slave->offset = cur_offset;
4758d2effeaSStefan Roese 		if (mtd_mod_by_eb(cur_offset, master) != 0) {
476e29c22f5SKyungmin Park 			/* Round up to next erasesize */
4778d2effeaSStefan Roese 			slave->offset = (mtd_div_by_eb(cur_offset, master) + 1) * master->erasesize;
478ff94bc40SHeiko Schocher 			debug("Moving partition %d: "
479ff94bc40SHeiko Schocher 			       "0x%012llx -> 0x%012llx\n", partno,
480ff94bc40SHeiko Schocher 			       (unsigned long long)cur_offset, (unsigned long long)slave->offset);
481ff94bc40SHeiko Schocher 		}
482ff94bc40SHeiko Schocher 	}
483ff94bc40SHeiko Schocher 	if (slave->offset == MTDPART_OFS_RETAIN) {
484ff94bc40SHeiko Schocher 		slave->offset = cur_offset;
485ff94bc40SHeiko Schocher 		if (master->size - slave->offset >= slave->mtd.size) {
486ff94bc40SHeiko Schocher 			slave->mtd.size = master->size - slave->offset
487ff94bc40SHeiko Schocher 							- slave->mtd.size;
488ff94bc40SHeiko Schocher 		} else {
489ff94bc40SHeiko Schocher 			debug("mtd partition \"%s\" doesn't have enough space: %#llx < %#llx, disabled\n",
490ff94bc40SHeiko Schocher 				part->name, master->size - slave->offset,
491ff94bc40SHeiko Schocher 				slave->mtd.size);
492ff94bc40SHeiko Schocher 			/* register to preserve ordering */
493ff94bc40SHeiko Schocher 			goto out_register;
494e29c22f5SKyungmin Park 		}
495e29c22f5SKyungmin Park 	}
496e29c22f5SKyungmin Park 	if (slave->mtd.size == MTDPART_SIZ_FULL)
497e29c22f5SKyungmin Park 		slave->mtd.size = master->size - slave->offset;
498e29c22f5SKyungmin Park 
499ff94bc40SHeiko Schocher 	debug("0x%012llx-0x%012llx : \"%s\"\n", (unsigned long long)slave->offset,
500ff94bc40SHeiko Schocher 		(unsigned long long)(slave->offset + slave->mtd.size), slave->mtd.name);
501e29c22f5SKyungmin Park 
502e29c22f5SKyungmin Park 	/* let's do some sanity checks */
503e29c22f5SKyungmin Park 	if (slave->offset >= master->size) {
504e29c22f5SKyungmin Park 		/* let's register it anyway to preserve ordering */
505e29c22f5SKyungmin Park 		slave->offset = 0;
506e29c22f5SKyungmin Park 		slave->mtd.size = 0;
5078d2effeaSStefan Roese 		printk(KERN_ERR"mtd: partition \"%s\" is out of reach -- disabled\n",
5088d2effeaSStefan Roese 			part->name);
5098d2effeaSStefan Roese 		goto out_register;
510e29c22f5SKyungmin Park 	}
511e29c22f5SKyungmin Park 	if (slave->offset + slave->mtd.size > master->size) {
512e29c22f5SKyungmin Park 		slave->mtd.size = master->size - slave->offset;
5138d2effeaSStefan Roese 		printk(KERN_WARNING"mtd: partition \"%s\" extends beyond the end of device \"%s\" -- size truncated to %#llx\n",
5148d2effeaSStefan Roese 			part->name, master->name, (unsigned long long)slave->mtd.size);
515e29c22f5SKyungmin Park 	}
516e29c22f5SKyungmin Park 	if (master->numeraseregions > 1) {
517e29c22f5SKyungmin Park 		/* Deal with variable erase size stuff */
5188d2effeaSStefan Roese 		int i, max = master->numeraseregions;
5198d2effeaSStefan Roese 		u64 end = slave->offset + slave->mtd.size;
520e29c22f5SKyungmin Park 		struct mtd_erase_region_info *regions = master->eraseregions;
521e29c22f5SKyungmin Park 
5228d2effeaSStefan Roese 		/* Find the first erase regions which is part of this
5238d2effeaSStefan Roese 		 * partition. */
5248d2effeaSStefan Roese 		for (i = 0; i < max && regions[i].offset <= slave->offset; i++)
525e29c22f5SKyungmin Park 			;
5268d2effeaSStefan Roese 		/* The loop searched for the region _behind_ the first one */
527ff94bc40SHeiko Schocher 		if (i > 0)
5288d2effeaSStefan Roese 			i--;
529e29c22f5SKyungmin Park 
5308d2effeaSStefan Roese 		/* Pick biggest erasesize */
5318d2effeaSStefan Roese 		for (; i < max && regions[i].offset < end; i++) {
532e29c22f5SKyungmin Park 			if (slave->mtd.erasesize < regions[i].erasesize) {
533e29c22f5SKyungmin Park 				slave->mtd.erasesize = regions[i].erasesize;
534e29c22f5SKyungmin Park 			}
535e29c22f5SKyungmin Park 		}
5368d2effeaSStefan Roese 		BUG_ON(slave->mtd.erasesize == 0);
537e29c22f5SKyungmin Park 	} else {
538e29c22f5SKyungmin Park 		/* Single erase size */
539e29c22f5SKyungmin Park 		slave->mtd.erasesize = master->erasesize;
540e29c22f5SKyungmin Park 	}
541e29c22f5SKyungmin Park 
542e29c22f5SKyungmin Park 	if ((slave->mtd.flags & MTD_WRITEABLE) &&
5438d2effeaSStefan Roese 	    mtd_mod_by_eb(slave->offset, &slave->mtd)) {
544e29c22f5SKyungmin Park 		/* Doesn't start on a boundary of major erase size */
5458d2effeaSStefan Roese 		/* FIXME: Let it be writable if it is on a boundary of
5468d2effeaSStefan Roese 		 * _minor_ erase size though */
547e29c22f5SKyungmin Park 		slave->mtd.flags &= ~MTD_WRITEABLE;
5488d2effeaSStefan Roese 		printk(KERN_WARNING"mtd: partition \"%s\" doesn't start on an erase block boundary -- force read-only\n",
5498d2effeaSStefan Roese 			part->name);
550e29c22f5SKyungmin Park 	}
551e29c22f5SKyungmin Park 	if ((slave->mtd.flags & MTD_WRITEABLE) &&
5528d2effeaSStefan Roese 	    mtd_mod_by_eb(slave->mtd.size, &slave->mtd)) {
553e29c22f5SKyungmin Park 		slave->mtd.flags &= ~MTD_WRITEABLE;
5548d2effeaSStefan Roese 		printk(KERN_WARNING"mtd: partition \"%s\" doesn't end on an erase block -- force read-only\n",
5558d2effeaSStefan Roese 			part->name);
556e29c22f5SKyungmin Park 	}
557e29c22f5SKyungmin Park 
558e29c22f5SKyungmin Park 	slave->mtd.ecclayout = master->ecclayout;
559ff94bc40SHeiko Schocher 	slave->mtd.ecc_step_size = master->ecc_step_size;
560ff94bc40SHeiko Schocher 	slave->mtd.ecc_strength = master->ecc_strength;
561ff94bc40SHeiko Schocher 	slave->mtd.bitflip_threshold = master->bitflip_threshold;
562ff94bc40SHeiko Schocher 
563dfe64e2cSSergey Lapin 	if (master->_block_isbad) {
5648d2effeaSStefan Roese 		uint64_t offs = 0;
565e29c22f5SKyungmin Park 
566e29c22f5SKyungmin Park 		while (offs < slave->mtd.size) {
567dfe64e2cSSergey Lapin 			if (mtd_block_isbad(master, offs + slave->offset))
568e29c22f5SKyungmin Park 				slave->mtd.ecc_stats.badblocks++;
569e29c22f5SKyungmin Park 			offs += slave->mtd.erasesize;
570e29c22f5SKyungmin Park 		}
571e29c22f5SKyungmin Park 	}
572e29c22f5SKyungmin Park 
5738d2effeaSStefan Roese out_register:
5748d2effeaSStefan Roese 	return slave;
5758d2effeaSStefan Roese }
5768d2effeaSStefan Roese 
577ddf7bcfaSHeiko Schocher #ifndef __UBOOT__
578ff94bc40SHeiko Schocher int mtd_add_partition(struct mtd_info *master, const char *name,
579ff94bc40SHeiko Schocher 		      long long offset, long long length)
580ff94bc40SHeiko Schocher {
581ff94bc40SHeiko Schocher 	struct mtd_partition part;
582ff94bc40SHeiko Schocher 	struct mtd_part *p, *new;
583ff94bc40SHeiko Schocher 	uint64_t start, end;
584ff94bc40SHeiko Schocher 	int ret = 0;
585ff94bc40SHeiko Schocher 
586ff94bc40SHeiko Schocher 	/* the direct offset is expected */
587ff94bc40SHeiko Schocher 	if (offset == MTDPART_OFS_APPEND ||
588ff94bc40SHeiko Schocher 	    offset == MTDPART_OFS_NXTBLK)
589ff94bc40SHeiko Schocher 		return -EINVAL;
590ff94bc40SHeiko Schocher 
591ff94bc40SHeiko Schocher 	if (length == MTDPART_SIZ_FULL)
592ff94bc40SHeiko Schocher 		length = master->size - offset;
593ff94bc40SHeiko Schocher 
594ff94bc40SHeiko Schocher 	if (length <= 0)
595ff94bc40SHeiko Schocher 		return -EINVAL;
596ff94bc40SHeiko Schocher 
597ff94bc40SHeiko Schocher 	part.name = name;
598ff94bc40SHeiko Schocher 	part.size = length;
599ff94bc40SHeiko Schocher 	part.offset = offset;
600ff94bc40SHeiko Schocher 	part.mask_flags = 0;
601ff94bc40SHeiko Schocher 	part.ecclayout = NULL;
602ff94bc40SHeiko Schocher 
603ff94bc40SHeiko Schocher 	new = allocate_partition(master, &part, -1, offset);
604ff94bc40SHeiko Schocher 	if (IS_ERR(new))
605ff94bc40SHeiko Schocher 		return PTR_ERR(new);
606ff94bc40SHeiko Schocher 
607ff94bc40SHeiko Schocher 	start = offset;
608ff94bc40SHeiko Schocher 	end = offset + length;
609ff94bc40SHeiko Schocher 
610ff94bc40SHeiko Schocher 	mutex_lock(&mtd_partitions_mutex);
611ff94bc40SHeiko Schocher 	list_for_each_entry(p, &mtd_partitions, list)
612ff94bc40SHeiko Schocher 		if (p->master == master) {
613ff94bc40SHeiko Schocher 			if ((start >= p->offset) &&
614ff94bc40SHeiko Schocher 			    (start < (p->offset + p->mtd.size)))
615ff94bc40SHeiko Schocher 				goto err_inv;
616ff94bc40SHeiko Schocher 
617ff94bc40SHeiko Schocher 			if ((end >= p->offset) &&
618ff94bc40SHeiko Schocher 			    (end < (p->offset + p->mtd.size)))
619ff94bc40SHeiko Schocher 				goto err_inv;
620ff94bc40SHeiko Schocher 		}
621ff94bc40SHeiko Schocher 
622ff94bc40SHeiko Schocher 	list_add(&new->list, &mtd_partitions);
623ff94bc40SHeiko Schocher 	mutex_unlock(&mtd_partitions_mutex);
624ff94bc40SHeiko Schocher 
625ff94bc40SHeiko Schocher 	add_mtd_device(&new->mtd);
626ff94bc40SHeiko Schocher 
627ff94bc40SHeiko Schocher 	return ret;
628ff94bc40SHeiko Schocher err_inv:
629ff94bc40SHeiko Schocher 	mutex_unlock(&mtd_partitions_mutex);
630ff94bc40SHeiko Schocher 	free_partition(new);
631ff94bc40SHeiko Schocher 	return -EINVAL;
632ff94bc40SHeiko Schocher }
633ff94bc40SHeiko Schocher EXPORT_SYMBOL_GPL(mtd_add_partition);
634ff94bc40SHeiko Schocher 
635ff94bc40SHeiko Schocher int mtd_del_partition(struct mtd_info *master, int partno)
636ff94bc40SHeiko Schocher {
637ff94bc40SHeiko Schocher 	struct mtd_part *slave, *next;
638ff94bc40SHeiko Schocher 	int ret = -EINVAL;
639ff94bc40SHeiko Schocher 
640ff94bc40SHeiko Schocher 	mutex_lock(&mtd_partitions_mutex);
641ff94bc40SHeiko Schocher 	list_for_each_entry_safe(slave, next, &mtd_partitions, list)
642ff94bc40SHeiko Schocher 		if ((slave->master == master) &&
643ff94bc40SHeiko Schocher 		    (slave->mtd.index == partno)) {
644ff94bc40SHeiko Schocher 			ret = del_mtd_device(&slave->mtd);
645ff94bc40SHeiko Schocher 			if (ret < 0)
646ff94bc40SHeiko Schocher 				break;
647ff94bc40SHeiko Schocher 
648ff94bc40SHeiko Schocher 			list_del(&slave->list);
649ff94bc40SHeiko Schocher 			free_partition(slave);
650ff94bc40SHeiko Schocher 			break;
651ff94bc40SHeiko Schocher 		}
652ff94bc40SHeiko Schocher 	mutex_unlock(&mtd_partitions_mutex);
653ff94bc40SHeiko Schocher 
654ff94bc40SHeiko Schocher 	return ret;
655ff94bc40SHeiko Schocher }
656ff94bc40SHeiko Schocher EXPORT_SYMBOL_GPL(mtd_del_partition);
657ddf7bcfaSHeiko Schocher #endif
658ff94bc40SHeiko Schocher 
6598d2effeaSStefan Roese /*
6608d2effeaSStefan Roese  * This function, given a master MTD object and a partition table, creates
6618d2effeaSStefan Roese  * and registers slave MTD objects which are bound to the master according to
6628d2effeaSStefan Roese  * the partition definitions.
6638d2effeaSStefan Roese  *
6648d2effeaSStefan Roese  * We don't register the master, or expect the caller to have done so,
6658d2effeaSStefan Roese  * for reasons of data integrity.
6668d2effeaSStefan Roese  */
6678d2effeaSStefan Roese 
6688d2effeaSStefan Roese int add_mtd_partitions(struct mtd_info *master,
6698d2effeaSStefan Roese 		       const struct mtd_partition *parts,
6708d2effeaSStefan Roese 		       int nbparts)
6718d2effeaSStefan Roese {
6728d2effeaSStefan Roese 	struct mtd_part *slave;
6738d2effeaSStefan Roese 	uint64_t cur_offset = 0;
6748d2effeaSStefan Roese 	int i;
6758d2effeaSStefan Roese 
676ff94bc40SHeiko Schocher #ifdef __UBOOT__
6778d2effeaSStefan Roese 	/*
6788d2effeaSStefan Roese 	 * Need to init the list here, since LIST_INIT() does not
6798d2effeaSStefan Roese 	 * work on platforms where relocation has problems (like MIPS
6808d2effeaSStefan Roese 	 * & PPC).
6818d2effeaSStefan Roese 	 */
6828d2effeaSStefan Roese 	if (mtd_partitions.next == NULL)
6838d2effeaSStefan Roese 		INIT_LIST_HEAD(&mtd_partitions);
684ff94bc40SHeiko Schocher #endif
6858d2effeaSStefan Roese 
686147162daSJoe Hershberger 	debug("Creating %d MTD partitions on \"%s\":\n", nbparts, master->name);
6878d2effeaSStefan Roese 
6888d2effeaSStefan Roese 	for (i = 0; i < nbparts; i++) {
689ff94bc40SHeiko Schocher 		slave = allocate_partition(master, parts + i, i, cur_offset);
690ff94bc40SHeiko Schocher 		if (IS_ERR(slave))
691ff94bc40SHeiko Schocher 			return PTR_ERR(slave);
692ff94bc40SHeiko Schocher 
693ff94bc40SHeiko Schocher 		mutex_lock(&mtd_partitions_mutex);
694ff94bc40SHeiko Schocher 		list_add(&slave->list, &mtd_partitions);
695ff94bc40SHeiko Schocher 		mutex_unlock(&mtd_partitions_mutex);
696ff94bc40SHeiko Schocher 
697ff94bc40SHeiko Schocher 		add_mtd_device(&slave->mtd);
698ff94bc40SHeiko Schocher 
6998d2effeaSStefan Roese 		cur_offset = slave->offset + slave->mtd.size;
700e29c22f5SKyungmin Park 	}
701e29c22f5SKyungmin Park 
702e29c22f5SKyungmin Park 	return 0;
703e29c22f5SKyungmin Park }
704ff94bc40SHeiko Schocher 
705ff94bc40SHeiko Schocher #ifndef __UBOOT__
706ff94bc40SHeiko Schocher static DEFINE_SPINLOCK(part_parser_lock);
707ff94bc40SHeiko Schocher static LIST_HEAD(part_parsers);
708ff94bc40SHeiko Schocher 
709ff94bc40SHeiko Schocher static struct mtd_part_parser *get_partition_parser(const char *name)
710ff94bc40SHeiko Schocher {
711ff94bc40SHeiko Schocher 	struct mtd_part_parser *p, *ret = NULL;
712ff94bc40SHeiko Schocher 
713ff94bc40SHeiko Schocher 	spin_lock(&part_parser_lock);
714ff94bc40SHeiko Schocher 
715ff94bc40SHeiko Schocher 	list_for_each_entry(p, &part_parsers, list)
716ff94bc40SHeiko Schocher 		if (!strcmp(p->name, name) && try_module_get(p->owner)) {
717ff94bc40SHeiko Schocher 			ret = p;
718ff94bc40SHeiko Schocher 			break;
719ff94bc40SHeiko Schocher 		}
720ff94bc40SHeiko Schocher 
721ff94bc40SHeiko Schocher 	spin_unlock(&part_parser_lock);
722ff94bc40SHeiko Schocher 
723ff94bc40SHeiko Schocher 	return ret;
724ff94bc40SHeiko Schocher }
725ff94bc40SHeiko Schocher 
726ff94bc40SHeiko Schocher #define put_partition_parser(p) do { module_put((p)->owner); } while (0)
727ff94bc40SHeiko Schocher 
728ff94bc40SHeiko Schocher void register_mtd_parser(struct mtd_part_parser *p)
729ff94bc40SHeiko Schocher {
730ff94bc40SHeiko Schocher 	spin_lock(&part_parser_lock);
731ff94bc40SHeiko Schocher 	list_add(&p->list, &part_parsers);
732ff94bc40SHeiko Schocher 	spin_unlock(&part_parser_lock);
733ff94bc40SHeiko Schocher }
734ff94bc40SHeiko Schocher EXPORT_SYMBOL_GPL(register_mtd_parser);
735ff94bc40SHeiko Schocher 
736ff94bc40SHeiko Schocher void deregister_mtd_parser(struct mtd_part_parser *p)
737ff94bc40SHeiko Schocher {
738ff94bc40SHeiko Schocher 	spin_lock(&part_parser_lock);
739ff94bc40SHeiko Schocher 	list_del(&p->list);
740ff94bc40SHeiko Schocher 	spin_unlock(&part_parser_lock);
741ff94bc40SHeiko Schocher }
742ff94bc40SHeiko Schocher EXPORT_SYMBOL_GPL(deregister_mtd_parser);
743ff94bc40SHeiko Schocher 
744ff94bc40SHeiko Schocher /*
745ff94bc40SHeiko Schocher  * Do not forget to update 'parse_mtd_partitions()' kerneldoc comment if you
746ff94bc40SHeiko Schocher  * are changing this array!
747ff94bc40SHeiko Schocher  */
748ff94bc40SHeiko Schocher static const char * const default_mtd_part_types[] = {
749ff94bc40SHeiko Schocher 	"cmdlinepart",
750ff94bc40SHeiko Schocher 	"ofpart",
751ff94bc40SHeiko Schocher 	NULL
752ff94bc40SHeiko Schocher };
753ff94bc40SHeiko Schocher 
754ff94bc40SHeiko Schocher /**
755ff94bc40SHeiko Schocher  * parse_mtd_partitions - parse MTD partitions
756ff94bc40SHeiko Schocher  * @master: the master partition (describes whole MTD device)
757ff94bc40SHeiko Schocher  * @types: names of partition parsers to try or %NULL
758ff94bc40SHeiko Schocher  * @pparts: array of partitions found is returned here
759ff94bc40SHeiko Schocher  * @data: MTD partition parser-specific data
760ff94bc40SHeiko Schocher  *
761ff94bc40SHeiko Schocher  * This function tries to find partition on MTD device @master. It uses MTD
762ff94bc40SHeiko Schocher  * partition parsers, specified in @types. However, if @types is %NULL, then
763ff94bc40SHeiko Schocher  * the default list of parsers is used. The default list contains only the
764ff94bc40SHeiko Schocher  * "cmdlinepart" and "ofpart" parsers ATM.
765ff94bc40SHeiko Schocher  * Note: If there are more then one parser in @types, the kernel only takes the
766ff94bc40SHeiko Schocher  * partitions parsed out by the first parser.
767ff94bc40SHeiko Schocher  *
768ff94bc40SHeiko Schocher  * This function may return:
769ff94bc40SHeiko Schocher  * o a negative error code in case of failure
770ff94bc40SHeiko Schocher  * o zero if no partitions were found
771ff94bc40SHeiko Schocher  * o a positive number of found partitions, in which case on exit @pparts will
772ff94bc40SHeiko Schocher  *   point to an array containing this number of &struct mtd_info objects.
773ff94bc40SHeiko Schocher  */
774ff94bc40SHeiko Schocher int parse_mtd_partitions(struct mtd_info *master, const char *const *types,
775ff94bc40SHeiko Schocher 			 struct mtd_partition **pparts,
776ff94bc40SHeiko Schocher 			 struct mtd_part_parser_data *data)
777ff94bc40SHeiko Schocher {
778ff94bc40SHeiko Schocher 	struct mtd_part_parser *parser;
779ff94bc40SHeiko Schocher 	int ret = 0;
780ff94bc40SHeiko Schocher 
781ff94bc40SHeiko Schocher 	if (!types)
782ff94bc40SHeiko Schocher 		types = default_mtd_part_types;
783ff94bc40SHeiko Schocher 
784ff94bc40SHeiko Schocher 	for ( ; ret <= 0 && *types; types++) {
785ff94bc40SHeiko Schocher 		parser = get_partition_parser(*types);
786ff94bc40SHeiko Schocher 		if (!parser && !request_module("%s", *types))
787ff94bc40SHeiko Schocher 			parser = get_partition_parser(*types);
788ff94bc40SHeiko Schocher 		if (!parser)
789ff94bc40SHeiko Schocher 			continue;
790ff94bc40SHeiko Schocher 		ret = (*parser->parse_fn)(master, pparts, data);
791ff94bc40SHeiko Schocher 		put_partition_parser(parser);
792ff94bc40SHeiko Schocher 		if (ret > 0) {
793ff94bc40SHeiko Schocher 			printk(KERN_NOTICE "%d %s partitions found on MTD device %s\n",
794ff94bc40SHeiko Schocher 			       ret, parser->name, master->name);
795ff94bc40SHeiko Schocher 			break;
796ff94bc40SHeiko Schocher 		}
797ff94bc40SHeiko Schocher 	}
798ff94bc40SHeiko Schocher 	return ret;
799ff94bc40SHeiko Schocher }
800ff94bc40SHeiko Schocher #endif
801ff94bc40SHeiko Schocher 
802ff94bc40SHeiko Schocher int mtd_is_partition(const struct mtd_info *mtd)
803ff94bc40SHeiko Schocher {
804ff94bc40SHeiko Schocher 	struct mtd_part *part;
805ff94bc40SHeiko Schocher 	int ispart = 0;
806ff94bc40SHeiko Schocher 
807ff94bc40SHeiko Schocher 	mutex_lock(&mtd_partitions_mutex);
808ff94bc40SHeiko Schocher 	list_for_each_entry(part, &mtd_partitions, list)
809ff94bc40SHeiko Schocher 		if (&part->mtd == mtd) {
810ff94bc40SHeiko Schocher 			ispart = 1;
811ff94bc40SHeiko Schocher 			break;
812ff94bc40SHeiko Schocher 		}
813ff94bc40SHeiko Schocher 	mutex_unlock(&mtd_partitions_mutex);
814ff94bc40SHeiko Schocher 
815ff94bc40SHeiko Schocher 	return ispart;
816ff94bc40SHeiko Schocher }
817ff94bc40SHeiko Schocher EXPORT_SYMBOL_GPL(mtd_is_partition);
818ff94bc40SHeiko Schocher 
819ff94bc40SHeiko Schocher /* Returns the size of the entire flash chip */
820ff94bc40SHeiko Schocher uint64_t mtd_get_device_size(const struct mtd_info *mtd)
821ff94bc40SHeiko Schocher {
822ff94bc40SHeiko Schocher 	if (!mtd_is_partition(mtd))
823ff94bc40SHeiko Schocher 		return mtd->size;
824ff94bc40SHeiko Schocher 
825ff94bc40SHeiko Schocher 	return PART(mtd)->master->size;
826ff94bc40SHeiko Schocher }
827ff94bc40SHeiko Schocher EXPORT_SYMBOL_GPL(mtd_get_device_size);
828