xref: /rk3399_rockchip-uboot/drivers/mtd/mtdpart.c (revision ddf7bcfa6c5a1d9647046c18e4945f0b0686aec5)
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 
181e29c22f5SKyungmin Park static int part_get_user_prot_info(struct mtd_info *mtd,
182e29c22f5SKyungmin Park 		struct otp_info *buf, size_t len)
183e29c22f5SKyungmin Park {
184e29c22f5SKyungmin Park 	struct mtd_part *part = PART(mtd);
185ff94bc40SHeiko Schocher 	return part->master->_get_user_prot_info(part->master, buf, len);
186e29c22f5SKyungmin Park }
187e29c22f5SKyungmin Park 
1888d2effeaSStefan Roese static int part_read_fact_prot_reg(struct mtd_info *mtd, loff_t from,
1898d2effeaSStefan Roese 		size_t len, size_t *retlen, u_char *buf)
190e29c22f5SKyungmin Park {
191e29c22f5SKyungmin Park 	struct mtd_part *part = PART(mtd);
192ff94bc40SHeiko Schocher 	return part->master->_read_fact_prot_reg(part->master, from, len,
193ff94bc40SHeiko Schocher 						 retlen, buf);
194e29c22f5SKyungmin Park }
195e29c22f5SKyungmin Park 
1968d2effeaSStefan Roese static int part_get_fact_prot_info(struct mtd_info *mtd, struct otp_info *buf,
1978d2effeaSStefan Roese 		size_t len)
198e29c22f5SKyungmin Park {
199e29c22f5SKyungmin Park 	struct mtd_part *part = PART(mtd);
200ff94bc40SHeiko Schocher 	return part->master->_get_fact_prot_info(part->master, buf, len);
201e29c22f5SKyungmin Park }
202e29c22f5SKyungmin Park 
203e29c22f5SKyungmin Park static int part_write(struct mtd_info *mtd, loff_t to, size_t len,
204e29c22f5SKyungmin Park 		size_t *retlen, const u_char *buf)
205e29c22f5SKyungmin Park {
206e29c22f5SKyungmin Park 	struct mtd_part *part = PART(mtd);
207ff94bc40SHeiko Schocher 	return part->master->_write(part->master, to + part->offset, len,
208ff94bc40SHeiko Schocher 				    retlen, buf);
209ff94bc40SHeiko Schocher }
210ff94bc40SHeiko Schocher 
211ff94bc40SHeiko Schocher static int part_panic_write(struct mtd_info *mtd, loff_t to, size_t len,
212ff94bc40SHeiko Schocher 		size_t *retlen, const u_char *buf)
213ff94bc40SHeiko Schocher {
214ff94bc40SHeiko Schocher 	struct mtd_part *part = PART(mtd);
215ff94bc40SHeiko Schocher 	return part->master->_panic_write(part->master, to + part->offset, len,
216ff94bc40SHeiko Schocher 					  retlen, buf);
217e29c22f5SKyungmin Park }
218e29c22f5SKyungmin Park 
219e29c22f5SKyungmin Park static int part_write_oob(struct mtd_info *mtd, loff_t to,
220e29c22f5SKyungmin Park 		struct mtd_oob_ops *ops)
221e29c22f5SKyungmin Park {
222e29c22f5SKyungmin Park 	struct mtd_part *part = PART(mtd);
223e29c22f5SKyungmin Park 
224e29c22f5SKyungmin Park 	if (to >= mtd->size)
225e29c22f5SKyungmin Park 		return -EINVAL;
226e29c22f5SKyungmin Park 	if (ops->datbuf && to + ops->len > mtd->size)
227e29c22f5SKyungmin Park 		return -EINVAL;
228ff94bc40SHeiko Schocher 	return part->master->_write_oob(part->master, to + part->offset, ops);
229e29c22f5SKyungmin Park }
230e29c22f5SKyungmin Park 
2318d2effeaSStefan Roese static int part_write_user_prot_reg(struct mtd_info *mtd, loff_t from,
2328d2effeaSStefan Roese 		size_t len, size_t *retlen, u_char *buf)
233e29c22f5SKyungmin Park {
234e29c22f5SKyungmin Park 	struct mtd_part *part = PART(mtd);
235ff94bc40SHeiko Schocher 	return part->master->_write_user_prot_reg(part->master, from, len,
236ff94bc40SHeiko Schocher 						  retlen, buf);
237e29c22f5SKyungmin Park }
238e29c22f5SKyungmin Park 
2398d2effeaSStefan Roese static int part_lock_user_prot_reg(struct mtd_info *mtd, loff_t from,
2408d2effeaSStefan Roese 		size_t len)
241e29c22f5SKyungmin Park {
242e29c22f5SKyungmin Park 	struct mtd_part *part = PART(mtd);
243ff94bc40SHeiko Schocher 	return part->master->_lock_user_prot_reg(part->master, from, len);
244e29c22f5SKyungmin Park }
245e29c22f5SKyungmin Park 
246ff94bc40SHeiko Schocher #ifndef __UBOOT__
247ff94bc40SHeiko Schocher static int part_writev(struct mtd_info *mtd, const struct kvec *vecs,
248ff94bc40SHeiko Schocher 		unsigned long count, loff_t to, size_t *retlen)
249ff94bc40SHeiko Schocher {
250ff94bc40SHeiko Schocher 	struct mtd_part *part = PART(mtd);
251ff94bc40SHeiko Schocher 	return part->master->_writev(part->master, vecs, count,
252ff94bc40SHeiko Schocher 				     to + part->offset, retlen);
253ff94bc40SHeiko Schocher }
254ff94bc40SHeiko Schocher #endif
255ff94bc40SHeiko Schocher 
256e29c22f5SKyungmin Park static int part_erase(struct mtd_info *mtd, struct erase_info *instr)
257e29c22f5SKyungmin Park {
258e29c22f5SKyungmin Park 	struct mtd_part *part = PART(mtd);
259e29c22f5SKyungmin Park 	int ret;
260dfe64e2cSSergey Lapin 
261e29c22f5SKyungmin Park 	instr->addr += part->offset;
262ff94bc40SHeiko Schocher 	ret = part->master->_erase(part->master, instr);
263e29c22f5SKyungmin Park 	if (ret) {
2648d2effeaSStefan Roese 		if (instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN)
265e29c22f5SKyungmin Park 			instr->fail_addr -= part->offset;
266e29c22f5SKyungmin Park 		instr->addr -= part->offset;
267e29c22f5SKyungmin Park 	}
268e29c22f5SKyungmin Park 	return ret;
269e29c22f5SKyungmin Park }
270e29c22f5SKyungmin Park 
271e29c22f5SKyungmin Park void mtd_erase_callback(struct erase_info *instr)
272e29c22f5SKyungmin Park {
273dfe64e2cSSergey Lapin 	if (instr->mtd->_erase == part_erase) {
274e29c22f5SKyungmin Park 		struct mtd_part *part = PART(instr->mtd);
275e29c22f5SKyungmin Park 
2768d2effeaSStefan Roese 		if (instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN)
277e29c22f5SKyungmin Park 			instr->fail_addr -= part->offset;
278e29c22f5SKyungmin Park 		instr->addr -= part->offset;
279e29c22f5SKyungmin Park 	}
280e29c22f5SKyungmin Park 	if (instr->callback)
281e29c22f5SKyungmin Park 		instr->callback(instr);
282e29c22f5SKyungmin Park }
283ff94bc40SHeiko Schocher EXPORT_SYMBOL_GPL(mtd_erase_callback);
284e29c22f5SKyungmin Park 
2858d2effeaSStefan Roese static int part_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
286e29c22f5SKyungmin Park {
287e29c22f5SKyungmin Park 	struct mtd_part *part = PART(mtd);
288ff94bc40SHeiko Schocher 	return part->master->_lock(part->master, ofs + part->offset, len);
289e29c22f5SKyungmin Park }
290e29c22f5SKyungmin Park 
2918d2effeaSStefan Roese static int part_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
292e29c22f5SKyungmin Park {
293e29c22f5SKyungmin Park 	struct mtd_part *part = PART(mtd);
294ff94bc40SHeiko Schocher 	return part->master->_unlock(part->master, ofs + part->offset, len);
295ff94bc40SHeiko Schocher }
296ff94bc40SHeiko Schocher 
297ff94bc40SHeiko Schocher static int part_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len)
298ff94bc40SHeiko Schocher {
299ff94bc40SHeiko Schocher 	struct mtd_part *part = PART(mtd);
300ff94bc40SHeiko Schocher 	return part->master->_is_locked(part->master, ofs + part->offset, len);
301e29c22f5SKyungmin Park }
302e29c22f5SKyungmin Park 
303e29c22f5SKyungmin Park static void part_sync(struct mtd_info *mtd)
304e29c22f5SKyungmin Park {
305e29c22f5SKyungmin Park 	struct mtd_part *part = PART(mtd);
306ff94bc40SHeiko Schocher 	part->master->_sync(part->master);
307e29c22f5SKyungmin Park }
308e29c22f5SKyungmin Park 
309ff94bc40SHeiko Schocher #ifndef __UBOOT__
310ff94bc40SHeiko Schocher static int part_suspend(struct mtd_info *mtd)
311ff94bc40SHeiko Schocher {
312ff94bc40SHeiko Schocher 	struct mtd_part *part = PART(mtd);
313ff94bc40SHeiko Schocher 	return part->master->_suspend(part->master);
314ff94bc40SHeiko Schocher }
315ff94bc40SHeiko Schocher 
316ff94bc40SHeiko Schocher static void part_resume(struct mtd_info *mtd)
317ff94bc40SHeiko Schocher {
318ff94bc40SHeiko Schocher 	struct mtd_part *part = PART(mtd);
319ff94bc40SHeiko Schocher 	part->master->_resume(part->master);
320ff94bc40SHeiko Schocher }
321ff94bc40SHeiko Schocher #endif
322ff94bc40SHeiko Schocher 
323e29c22f5SKyungmin Park static int part_block_isbad(struct mtd_info *mtd, loff_t ofs)
324e29c22f5SKyungmin Park {
325e29c22f5SKyungmin Park 	struct mtd_part *part = PART(mtd);
326e29c22f5SKyungmin Park 	ofs += part->offset;
327ff94bc40SHeiko Schocher 	return part->master->_block_isbad(part->master, ofs);
328e29c22f5SKyungmin Park }
329e29c22f5SKyungmin Park 
330e29c22f5SKyungmin Park static int part_block_markbad(struct mtd_info *mtd, loff_t ofs)
331e29c22f5SKyungmin Park {
332e29c22f5SKyungmin Park 	struct mtd_part *part = PART(mtd);
333e29c22f5SKyungmin Park 	int res;
334e29c22f5SKyungmin Park 
335e29c22f5SKyungmin Park 	ofs += part->offset;
336ff94bc40SHeiko Schocher 	res = part->master->_block_markbad(part->master, ofs);
337e29c22f5SKyungmin Park 	if (!res)
338e29c22f5SKyungmin Park 		mtd->ecc_stats.badblocks++;
339e29c22f5SKyungmin Park 	return res;
340e29c22f5SKyungmin Park }
341e29c22f5SKyungmin Park 
342ff94bc40SHeiko Schocher static inline void free_partition(struct mtd_part *p)
343ff94bc40SHeiko Schocher {
344ff94bc40SHeiko Schocher 	kfree(p->mtd.name);
345ff94bc40SHeiko Schocher 	kfree(p);
346ff94bc40SHeiko Schocher }
347ff94bc40SHeiko Schocher 
348e29c22f5SKyungmin Park /*
349e29c22f5SKyungmin Park  * This function unregisters and destroy all slave MTD objects which are
350e29c22f5SKyungmin Park  * attached to the given master MTD object.
351e29c22f5SKyungmin Park  */
352e29c22f5SKyungmin Park 
353e29c22f5SKyungmin Park int del_mtd_partitions(struct mtd_info *master)
354e29c22f5SKyungmin Park {
3558d2effeaSStefan Roese 	struct mtd_part *slave, *next;
356ff94bc40SHeiko Schocher 	int ret, err = 0;
357e29c22f5SKyungmin Park 
358ff94bc40SHeiko Schocher 	mutex_lock(&mtd_partitions_mutex);
3598d2effeaSStefan Roese 	list_for_each_entry_safe(slave, next, &mtd_partitions, list)
360e29c22f5SKyungmin Park 		if (slave->master == master) {
361ff94bc40SHeiko Schocher 			ret = del_mtd_device(&slave->mtd);
362ff94bc40SHeiko Schocher 			if (ret < 0) {
363ff94bc40SHeiko Schocher 				err = ret;
364ff94bc40SHeiko Schocher 				continue;
365ff94bc40SHeiko Schocher 			}
3668d2effeaSStefan Roese 			list_del(&slave->list);
367ff94bc40SHeiko Schocher 			free_partition(slave);
368ff94bc40SHeiko Schocher 		}
369ff94bc40SHeiko Schocher 	mutex_unlock(&mtd_partitions_mutex);
370ff94bc40SHeiko Schocher 
371ff94bc40SHeiko Schocher 	return err;
372e29c22f5SKyungmin Park }
373e29c22f5SKyungmin Park 
374ff94bc40SHeiko Schocher static struct mtd_part *allocate_partition(struct mtd_info *master,
3758d2effeaSStefan Roese 			const struct mtd_partition *part, int partno,
3768d2effeaSStefan Roese 			uint64_t cur_offset)
377e29c22f5SKyungmin Park {
378e29c22f5SKyungmin Park 	struct mtd_part *slave;
379ff94bc40SHeiko Schocher 	char *name;
380e29c22f5SKyungmin Park 
381e29c22f5SKyungmin Park 	/* allocate the partition structure */
382e29c22f5SKyungmin Park 	slave = kzalloc(sizeof(*slave), GFP_KERNEL);
383ff94bc40SHeiko Schocher 	name = kstrdup(part->name, GFP_KERNEL);
384ff94bc40SHeiko Schocher 	if (!name || !slave) {
3858d2effeaSStefan Roese 		printk(KERN_ERR"memory allocation error while creating partitions for \"%s\"\n",
386e29c22f5SKyungmin Park 		       master->name);
387ff94bc40SHeiko Schocher 		kfree(name);
388ff94bc40SHeiko Schocher 		kfree(slave);
389ff94bc40SHeiko Schocher 		return ERR_PTR(-ENOMEM);
390e29c22f5SKyungmin Park 	}
391e29c22f5SKyungmin Park 
392e29c22f5SKyungmin Park 	/* set up the MTD object for this partition */
393e29c22f5SKyungmin Park 	slave->mtd.type = master->type;
3948d2effeaSStefan Roese 	slave->mtd.flags = master->flags & ~part->mask_flags;
3958d2effeaSStefan Roese 	slave->mtd.size = part->size;
396e29c22f5SKyungmin Park 	slave->mtd.writesize = master->writesize;
397ff94bc40SHeiko Schocher 	slave->mtd.writebufsize = master->writebufsize;
398e29c22f5SKyungmin Park 	slave->mtd.oobsize = master->oobsize;
399e29c22f5SKyungmin Park 	slave->mtd.oobavail = master->oobavail;
400e29c22f5SKyungmin Park 	slave->mtd.subpage_sft = master->subpage_sft;
401e29c22f5SKyungmin Park 
402ff94bc40SHeiko Schocher 	slave->mtd.name = name;
403e29c22f5SKyungmin Park 	slave->mtd.owner = master->owner;
404ff94bc40SHeiko Schocher #ifndef __UBOOT__
405ff94bc40SHeiko Schocher 	slave->mtd.backing_dev_info = master->backing_dev_info;
406ff94bc40SHeiko Schocher 
407ff94bc40SHeiko Schocher 	/* NOTE:  we don't arrange MTDs as a tree; it'd be error-prone
408ff94bc40SHeiko Schocher 	 * to have the same data be in two different partitions.
409ff94bc40SHeiko Schocher 	 */
410ff94bc40SHeiko Schocher 	slave->mtd.dev.parent = master->dev.parent;
411ff94bc40SHeiko Schocher #endif
412e29c22f5SKyungmin Park 
413dfe64e2cSSergey Lapin 	slave->mtd._read = part_read;
414dfe64e2cSSergey Lapin 	slave->mtd._write = part_write;
415e29c22f5SKyungmin Park 
416ff94bc40SHeiko Schocher 	if (master->_panic_write)
417ff94bc40SHeiko Schocher 		slave->mtd._panic_write = part_panic_write;
418ff94bc40SHeiko Schocher 
419ff94bc40SHeiko Schocher #ifndef __UBOOT__
420ff94bc40SHeiko Schocher 	if (master->_point && master->_unpoint) {
421ff94bc40SHeiko Schocher 		slave->mtd._point = part_point;
422ff94bc40SHeiko Schocher 		slave->mtd._unpoint = part_unpoint;
423ff94bc40SHeiko Schocher 	}
424ff94bc40SHeiko Schocher #endif
425ff94bc40SHeiko Schocher 
426ff94bc40SHeiko Schocher 	if (master->_get_unmapped_area)
427ff94bc40SHeiko Schocher 		slave->mtd._get_unmapped_area = part_get_unmapped_area;
428dfe64e2cSSergey Lapin 	if (master->_read_oob)
429dfe64e2cSSergey Lapin 		slave->mtd._read_oob = part_read_oob;
430dfe64e2cSSergey Lapin 	if (master->_write_oob)
431dfe64e2cSSergey Lapin 		slave->mtd._write_oob = part_write_oob;
432dfe64e2cSSergey Lapin 	if (master->_read_user_prot_reg)
433dfe64e2cSSergey Lapin 		slave->mtd._read_user_prot_reg = part_read_user_prot_reg;
434dfe64e2cSSergey Lapin 	if (master->_read_fact_prot_reg)
435dfe64e2cSSergey Lapin 		slave->mtd._read_fact_prot_reg = part_read_fact_prot_reg;
436dfe64e2cSSergey Lapin 	if (master->_write_user_prot_reg)
437dfe64e2cSSergey Lapin 		slave->mtd._write_user_prot_reg = part_write_user_prot_reg;
438dfe64e2cSSergey Lapin 	if (master->_lock_user_prot_reg)
439dfe64e2cSSergey Lapin 		slave->mtd._lock_user_prot_reg = part_lock_user_prot_reg;
440dfe64e2cSSergey Lapin 	if (master->_get_user_prot_info)
441dfe64e2cSSergey Lapin 		slave->mtd._get_user_prot_info = part_get_user_prot_info;
442dfe64e2cSSergey Lapin 	if (master->_get_fact_prot_info)
443dfe64e2cSSergey Lapin 		slave->mtd._get_fact_prot_info = part_get_fact_prot_info;
444dfe64e2cSSergey Lapin 	if (master->_sync)
445dfe64e2cSSergey Lapin 		slave->mtd._sync = part_sync;
446ff94bc40SHeiko Schocher #ifndef __UBOOT__
447ff94bc40SHeiko Schocher 	if (!partno && !master->dev.class && master->_suspend &&
448ff94bc40SHeiko Schocher 	    master->_resume) {
449ff94bc40SHeiko Schocher 			slave->mtd._suspend = part_suspend;
450ff94bc40SHeiko Schocher 			slave->mtd._resume = part_resume;
451ff94bc40SHeiko Schocher 	}
452ff94bc40SHeiko Schocher 	if (master->_writev)
453ff94bc40SHeiko Schocher 		slave->mtd._writev = part_writev;
454ff94bc40SHeiko Schocher #endif
455dfe64e2cSSergey Lapin 	if (master->_lock)
456dfe64e2cSSergey Lapin 		slave->mtd._lock = part_lock;
457dfe64e2cSSergey Lapin 	if (master->_unlock)
458dfe64e2cSSergey Lapin 		slave->mtd._unlock = part_unlock;
459ff94bc40SHeiko Schocher 	if (master->_is_locked)
460ff94bc40SHeiko Schocher 		slave->mtd._is_locked = part_is_locked;
461dfe64e2cSSergey Lapin 	if (master->_block_isbad)
462dfe64e2cSSergey Lapin 		slave->mtd._block_isbad = part_block_isbad;
463dfe64e2cSSergey Lapin 	if (master->_block_markbad)
464dfe64e2cSSergey Lapin 		slave->mtd._block_markbad = part_block_markbad;
465dfe64e2cSSergey Lapin 	slave->mtd._erase = part_erase;
466e29c22f5SKyungmin Park 	slave->master = master;
4678d2effeaSStefan Roese 	slave->offset = part->offset;
468e29c22f5SKyungmin Park 
469e29c22f5SKyungmin Park 	if (slave->offset == MTDPART_OFS_APPEND)
470e29c22f5SKyungmin Park 		slave->offset = cur_offset;
471e29c22f5SKyungmin Park 	if (slave->offset == MTDPART_OFS_NXTBLK) {
472e29c22f5SKyungmin Park 		slave->offset = cur_offset;
4738d2effeaSStefan Roese 		if (mtd_mod_by_eb(cur_offset, master) != 0) {
474e29c22f5SKyungmin Park 			/* Round up to next erasesize */
4758d2effeaSStefan Roese 			slave->offset = (mtd_div_by_eb(cur_offset, master) + 1) * master->erasesize;
476ff94bc40SHeiko Schocher 			debug("Moving partition %d: "
477ff94bc40SHeiko Schocher 			       "0x%012llx -> 0x%012llx\n", partno,
478ff94bc40SHeiko Schocher 			       (unsigned long long)cur_offset, (unsigned long long)slave->offset);
479ff94bc40SHeiko Schocher 		}
480ff94bc40SHeiko Schocher 	}
481ff94bc40SHeiko Schocher 	if (slave->offset == MTDPART_OFS_RETAIN) {
482ff94bc40SHeiko Schocher 		slave->offset = cur_offset;
483ff94bc40SHeiko Schocher 		if (master->size - slave->offset >= slave->mtd.size) {
484ff94bc40SHeiko Schocher 			slave->mtd.size = master->size - slave->offset
485ff94bc40SHeiko Schocher 							- slave->mtd.size;
486ff94bc40SHeiko Schocher 		} else {
487ff94bc40SHeiko Schocher 			debug("mtd partition \"%s\" doesn't have enough space: %#llx < %#llx, disabled\n",
488ff94bc40SHeiko Schocher 				part->name, master->size - slave->offset,
489ff94bc40SHeiko Schocher 				slave->mtd.size);
490ff94bc40SHeiko Schocher 			/* register to preserve ordering */
491ff94bc40SHeiko Schocher 			goto out_register;
492e29c22f5SKyungmin Park 		}
493e29c22f5SKyungmin Park 	}
494e29c22f5SKyungmin Park 	if (slave->mtd.size == MTDPART_SIZ_FULL)
495e29c22f5SKyungmin Park 		slave->mtd.size = master->size - slave->offset;
496e29c22f5SKyungmin Park 
497ff94bc40SHeiko Schocher 	debug("0x%012llx-0x%012llx : \"%s\"\n", (unsigned long long)slave->offset,
498ff94bc40SHeiko Schocher 		(unsigned long long)(slave->offset + slave->mtd.size), slave->mtd.name);
499e29c22f5SKyungmin Park 
500e29c22f5SKyungmin Park 	/* let's do some sanity checks */
501e29c22f5SKyungmin Park 	if (slave->offset >= master->size) {
502e29c22f5SKyungmin Park 		/* let's register it anyway to preserve ordering */
503e29c22f5SKyungmin Park 		slave->offset = 0;
504e29c22f5SKyungmin Park 		slave->mtd.size = 0;
5058d2effeaSStefan Roese 		printk(KERN_ERR"mtd: partition \"%s\" is out of reach -- disabled\n",
5068d2effeaSStefan Roese 			part->name);
5078d2effeaSStefan Roese 		goto out_register;
508e29c22f5SKyungmin Park 	}
509e29c22f5SKyungmin Park 	if (slave->offset + slave->mtd.size > master->size) {
510e29c22f5SKyungmin Park 		slave->mtd.size = master->size - slave->offset;
5118d2effeaSStefan Roese 		printk(KERN_WARNING"mtd: partition \"%s\" extends beyond the end of device \"%s\" -- size truncated to %#llx\n",
5128d2effeaSStefan Roese 			part->name, master->name, (unsigned long long)slave->mtd.size);
513e29c22f5SKyungmin Park 	}
514e29c22f5SKyungmin Park 	if (master->numeraseregions > 1) {
515e29c22f5SKyungmin Park 		/* Deal with variable erase size stuff */
5168d2effeaSStefan Roese 		int i, max = master->numeraseregions;
5178d2effeaSStefan Roese 		u64 end = slave->offset + slave->mtd.size;
518e29c22f5SKyungmin Park 		struct mtd_erase_region_info *regions = master->eraseregions;
519e29c22f5SKyungmin Park 
5208d2effeaSStefan Roese 		/* Find the first erase regions which is part of this
5218d2effeaSStefan Roese 		 * partition. */
5228d2effeaSStefan Roese 		for (i = 0; i < max && regions[i].offset <= slave->offset; i++)
523e29c22f5SKyungmin Park 			;
5248d2effeaSStefan Roese 		/* The loop searched for the region _behind_ the first one */
525ff94bc40SHeiko Schocher 		if (i > 0)
5268d2effeaSStefan Roese 			i--;
527e29c22f5SKyungmin Park 
5288d2effeaSStefan Roese 		/* Pick biggest erasesize */
5298d2effeaSStefan Roese 		for (; i < max && regions[i].offset < end; i++) {
530e29c22f5SKyungmin Park 			if (slave->mtd.erasesize < regions[i].erasesize) {
531e29c22f5SKyungmin Park 				slave->mtd.erasesize = regions[i].erasesize;
532e29c22f5SKyungmin Park 			}
533e29c22f5SKyungmin Park 		}
5348d2effeaSStefan Roese 		BUG_ON(slave->mtd.erasesize == 0);
535e29c22f5SKyungmin Park 	} else {
536e29c22f5SKyungmin Park 		/* Single erase size */
537e29c22f5SKyungmin Park 		slave->mtd.erasesize = master->erasesize;
538e29c22f5SKyungmin Park 	}
539e29c22f5SKyungmin Park 
540e29c22f5SKyungmin Park 	if ((slave->mtd.flags & MTD_WRITEABLE) &&
5418d2effeaSStefan Roese 	    mtd_mod_by_eb(slave->offset, &slave->mtd)) {
542e29c22f5SKyungmin Park 		/* Doesn't start on a boundary of major erase size */
5438d2effeaSStefan Roese 		/* FIXME: Let it be writable if it is on a boundary of
5448d2effeaSStefan Roese 		 * _minor_ erase size though */
545e29c22f5SKyungmin Park 		slave->mtd.flags &= ~MTD_WRITEABLE;
5468d2effeaSStefan Roese 		printk(KERN_WARNING"mtd: partition \"%s\" doesn't start on an erase block boundary -- force read-only\n",
5478d2effeaSStefan Roese 			part->name);
548e29c22f5SKyungmin Park 	}
549e29c22f5SKyungmin Park 	if ((slave->mtd.flags & MTD_WRITEABLE) &&
5508d2effeaSStefan Roese 	    mtd_mod_by_eb(slave->mtd.size, &slave->mtd)) {
551e29c22f5SKyungmin Park 		slave->mtd.flags &= ~MTD_WRITEABLE;
5528d2effeaSStefan Roese 		printk(KERN_WARNING"mtd: partition \"%s\" doesn't end on an erase block -- force read-only\n",
5538d2effeaSStefan Roese 			part->name);
554e29c22f5SKyungmin Park 	}
555e29c22f5SKyungmin Park 
556e29c22f5SKyungmin Park 	slave->mtd.ecclayout = master->ecclayout;
557ff94bc40SHeiko Schocher 	slave->mtd.ecc_step_size = master->ecc_step_size;
558ff94bc40SHeiko Schocher 	slave->mtd.ecc_strength = master->ecc_strength;
559ff94bc40SHeiko Schocher 	slave->mtd.bitflip_threshold = master->bitflip_threshold;
560ff94bc40SHeiko Schocher 
561dfe64e2cSSergey Lapin 	if (master->_block_isbad) {
5628d2effeaSStefan Roese 		uint64_t offs = 0;
563e29c22f5SKyungmin Park 
564e29c22f5SKyungmin Park 		while (offs < slave->mtd.size) {
565dfe64e2cSSergey Lapin 			if (mtd_block_isbad(master, offs + slave->offset))
566e29c22f5SKyungmin Park 				slave->mtd.ecc_stats.badblocks++;
567e29c22f5SKyungmin Park 			offs += slave->mtd.erasesize;
568e29c22f5SKyungmin Park 		}
569e29c22f5SKyungmin Park 	}
570e29c22f5SKyungmin Park 
5718d2effeaSStefan Roese out_register:
5728d2effeaSStefan Roese 	return slave;
5738d2effeaSStefan Roese }
5748d2effeaSStefan Roese 
575*ddf7bcfaSHeiko Schocher #ifndef __UBOOT__
576ff94bc40SHeiko Schocher int mtd_add_partition(struct mtd_info *master, const char *name,
577ff94bc40SHeiko Schocher 		      long long offset, long long length)
578ff94bc40SHeiko Schocher {
579ff94bc40SHeiko Schocher 	struct mtd_partition part;
580ff94bc40SHeiko Schocher 	struct mtd_part *p, *new;
581ff94bc40SHeiko Schocher 	uint64_t start, end;
582ff94bc40SHeiko Schocher 	int ret = 0;
583ff94bc40SHeiko Schocher 
584ff94bc40SHeiko Schocher 	/* the direct offset is expected */
585ff94bc40SHeiko Schocher 	if (offset == MTDPART_OFS_APPEND ||
586ff94bc40SHeiko Schocher 	    offset == MTDPART_OFS_NXTBLK)
587ff94bc40SHeiko Schocher 		return -EINVAL;
588ff94bc40SHeiko Schocher 
589ff94bc40SHeiko Schocher 	if (length == MTDPART_SIZ_FULL)
590ff94bc40SHeiko Schocher 		length = master->size - offset;
591ff94bc40SHeiko Schocher 
592ff94bc40SHeiko Schocher 	if (length <= 0)
593ff94bc40SHeiko Schocher 		return -EINVAL;
594ff94bc40SHeiko Schocher 
595ff94bc40SHeiko Schocher 	part.name = name;
596ff94bc40SHeiko Schocher 	part.size = length;
597ff94bc40SHeiko Schocher 	part.offset = offset;
598ff94bc40SHeiko Schocher 	part.mask_flags = 0;
599ff94bc40SHeiko Schocher 	part.ecclayout = NULL;
600ff94bc40SHeiko Schocher 
601ff94bc40SHeiko Schocher 	new = allocate_partition(master, &part, -1, offset);
602ff94bc40SHeiko Schocher 	if (IS_ERR(new))
603ff94bc40SHeiko Schocher 		return PTR_ERR(new);
604ff94bc40SHeiko Schocher 
605ff94bc40SHeiko Schocher 	start = offset;
606ff94bc40SHeiko Schocher 	end = offset + length;
607ff94bc40SHeiko Schocher 
608ff94bc40SHeiko Schocher 	mutex_lock(&mtd_partitions_mutex);
609ff94bc40SHeiko Schocher 	list_for_each_entry(p, &mtd_partitions, list)
610ff94bc40SHeiko Schocher 		if (p->master == master) {
611ff94bc40SHeiko Schocher 			if ((start >= p->offset) &&
612ff94bc40SHeiko Schocher 			    (start < (p->offset + p->mtd.size)))
613ff94bc40SHeiko Schocher 				goto err_inv;
614ff94bc40SHeiko Schocher 
615ff94bc40SHeiko Schocher 			if ((end >= p->offset) &&
616ff94bc40SHeiko Schocher 			    (end < (p->offset + p->mtd.size)))
617ff94bc40SHeiko Schocher 				goto err_inv;
618ff94bc40SHeiko Schocher 		}
619ff94bc40SHeiko Schocher 
620ff94bc40SHeiko Schocher 	list_add(&new->list, &mtd_partitions);
621ff94bc40SHeiko Schocher 	mutex_unlock(&mtd_partitions_mutex);
622ff94bc40SHeiko Schocher 
623ff94bc40SHeiko Schocher 	add_mtd_device(&new->mtd);
624ff94bc40SHeiko Schocher 
625ff94bc40SHeiko Schocher 	return ret;
626ff94bc40SHeiko Schocher err_inv:
627ff94bc40SHeiko Schocher 	mutex_unlock(&mtd_partitions_mutex);
628ff94bc40SHeiko Schocher 	free_partition(new);
629ff94bc40SHeiko Schocher 	return -EINVAL;
630ff94bc40SHeiko Schocher }
631ff94bc40SHeiko Schocher EXPORT_SYMBOL_GPL(mtd_add_partition);
632ff94bc40SHeiko Schocher 
633ff94bc40SHeiko Schocher int mtd_del_partition(struct mtd_info *master, int partno)
634ff94bc40SHeiko Schocher {
635ff94bc40SHeiko Schocher 	struct mtd_part *slave, *next;
636ff94bc40SHeiko Schocher 	int ret = -EINVAL;
637ff94bc40SHeiko Schocher 
638ff94bc40SHeiko Schocher 	mutex_lock(&mtd_partitions_mutex);
639ff94bc40SHeiko Schocher 	list_for_each_entry_safe(slave, next, &mtd_partitions, list)
640ff94bc40SHeiko Schocher 		if ((slave->master == master) &&
641ff94bc40SHeiko Schocher 		    (slave->mtd.index == partno)) {
642ff94bc40SHeiko Schocher 			ret = del_mtd_device(&slave->mtd);
643ff94bc40SHeiko Schocher 			if (ret < 0)
644ff94bc40SHeiko Schocher 				break;
645ff94bc40SHeiko Schocher 
646ff94bc40SHeiko Schocher 			list_del(&slave->list);
647ff94bc40SHeiko Schocher 			free_partition(slave);
648ff94bc40SHeiko Schocher 			break;
649ff94bc40SHeiko Schocher 		}
650ff94bc40SHeiko Schocher 	mutex_unlock(&mtd_partitions_mutex);
651ff94bc40SHeiko Schocher 
652ff94bc40SHeiko Schocher 	return ret;
653ff94bc40SHeiko Schocher }
654ff94bc40SHeiko Schocher EXPORT_SYMBOL_GPL(mtd_del_partition);
655*ddf7bcfaSHeiko Schocher #endif
656ff94bc40SHeiko Schocher 
6578d2effeaSStefan Roese /*
6588d2effeaSStefan Roese  * This function, given a master MTD object and a partition table, creates
6598d2effeaSStefan Roese  * and registers slave MTD objects which are bound to the master according to
6608d2effeaSStefan Roese  * the partition definitions.
6618d2effeaSStefan Roese  *
6628d2effeaSStefan Roese  * We don't register the master, or expect the caller to have done so,
6638d2effeaSStefan Roese  * for reasons of data integrity.
6648d2effeaSStefan Roese  */
6658d2effeaSStefan Roese 
6668d2effeaSStefan Roese int add_mtd_partitions(struct mtd_info *master,
6678d2effeaSStefan Roese 		       const struct mtd_partition *parts,
6688d2effeaSStefan Roese 		       int nbparts)
6698d2effeaSStefan Roese {
6708d2effeaSStefan Roese 	struct mtd_part *slave;
6718d2effeaSStefan Roese 	uint64_t cur_offset = 0;
6728d2effeaSStefan Roese 	int i;
6738d2effeaSStefan Roese 
674ff94bc40SHeiko Schocher #ifdef __UBOOT__
6758d2effeaSStefan Roese 	/*
6768d2effeaSStefan Roese 	 * Need to init the list here, since LIST_INIT() does not
6778d2effeaSStefan Roese 	 * work on platforms where relocation has problems (like MIPS
6788d2effeaSStefan Roese 	 * & PPC).
6798d2effeaSStefan Roese 	 */
6808d2effeaSStefan Roese 	if (mtd_partitions.next == NULL)
6818d2effeaSStefan Roese 		INIT_LIST_HEAD(&mtd_partitions);
682ff94bc40SHeiko Schocher #endif
6838d2effeaSStefan Roese 
684147162daSJoe Hershberger 	debug("Creating %d MTD partitions on \"%s\":\n", nbparts, master->name);
6858d2effeaSStefan Roese 
6868d2effeaSStefan Roese 	for (i = 0; i < nbparts; i++) {
687ff94bc40SHeiko Schocher 		slave = allocate_partition(master, parts + i, i, cur_offset);
688ff94bc40SHeiko Schocher 		if (IS_ERR(slave))
689ff94bc40SHeiko Schocher 			return PTR_ERR(slave);
690ff94bc40SHeiko Schocher 
691ff94bc40SHeiko Schocher 		mutex_lock(&mtd_partitions_mutex);
692ff94bc40SHeiko Schocher 		list_add(&slave->list, &mtd_partitions);
693ff94bc40SHeiko Schocher 		mutex_unlock(&mtd_partitions_mutex);
694ff94bc40SHeiko Schocher 
695ff94bc40SHeiko Schocher 		add_mtd_device(&slave->mtd);
696ff94bc40SHeiko Schocher 
6978d2effeaSStefan Roese 		cur_offset = slave->offset + slave->mtd.size;
698e29c22f5SKyungmin Park 	}
699e29c22f5SKyungmin Park 
700e29c22f5SKyungmin Park 	return 0;
701e29c22f5SKyungmin Park }
702ff94bc40SHeiko Schocher 
703ff94bc40SHeiko Schocher #ifndef __UBOOT__
704ff94bc40SHeiko Schocher static DEFINE_SPINLOCK(part_parser_lock);
705ff94bc40SHeiko Schocher static LIST_HEAD(part_parsers);
706ff94bc40SHeiko Schocher 
707ff94bc40SHeiko Schocher static struct mtd_part_parser *get_partition_parser(const char *name)
708ff94bc40SHeiko Schocher {
709ff94bc40SHeiko Schocher 	struct mtd_part_parser *p, *ret = NULL;
710ff94bc40SHeiko Schocher 
711ff94bc40SHeiko Schocher 	spin_lock(&part_parser_lock);
712ff94bc40SHeiko Schocher 
713ff94bc40SHeiko Schocher 	list_for_each_entry(p, &part_parsers, list)
714ff94bc40SHeiko Schocher 		if (!strcmp(p->name, name) && try_module_get(p->owner)) {
715ff94bc40SHeiko Schocher 			ret = p;
716ff94bc40SHeiko Schocher 			break;
717ff94bc40SHeiko Schocher 		}
718ff94bc40SHeiko Schocher 
719ff94bc40SHeiko Schocher 	spin_unlock(&part_parser_lock);
720ff94bc40SHeiko Schocher 
721ff94bc40SHeiko Schocher 	return ret;
722ff94bc40SHeiko Schocher }
723ff94bc40SHeiko Schocher 
724ff94bc40SHeiko Schocher #define put_partition_parser(p) do { module_put((p)->owner); } while (0)
725ff94bc40SHeiko Schocher 
726ff94bc40SHeiko Schocher void register_mtd_parser(struct mtd_part_parser *p)
727ff94bc40SHeiko Schocher {
728ff94bc40SHeiko Schocher 	spin_lock(&part_parser_lock);
729ff94bc40SHeiko Schocher 	list_add(&p->list, &part_parsers);
730ff94bc40SHeiko Schocher 	spin_unlock(&part_parser_lock);
731ff94bc40SHeiko Schocher }
732ff94bc40SHeiko Schocher EXPORT_SYMBOL_GPL(register_mtd_parser);
733ff94bc40SHeiko Schocher 
734ff94bc40SHeiko Schocher void deregister_mtd_parser(struct mtd_part_parser *p)
735ff94bc40SHeiko Schocher {
736ff94bc40SHeiko Schocher 	spin_lock(&part_parser_lock);
737ff94bc40SHeiko Schocher 	list_del(&p->list);
738ff94bc40SHeiko Schocher 	spin_unlock(&part_parser_lock);
739ff94bc40SHeiko Schocher }
740ff94bc40SHeiko Schocher EXPORT_SYMBOL_GPL(deregister_mtd_parser);
741ff94bc40SHeiko Schocher 
742ff94bc40SHeiko Schocher /*
743ff94bc40SHeiko Schocher  * Do not forget to update 'parse_mtd_partitions()' kerneldoc comment if you
744ff94bc40SHeiko Schocher  * are changing this array!
745ff94bc40SHeiko Schocher  */
746ff94bc40SHeiko Schocher static const char * const default_mtd_part_types[] = {
747ff94bc40SHeiko Schocher 	"cmdlinepart",
748ff94bc40SHeiko Schocher 	"ofpart",
749ff94bc40SHeiko Schocher 	NULL
750ff94bc40SHeiko Schocher };
751ff94bc40SHeiko Schocher 
752ff94bc40SHeiko Schocher /**
753ff94bc40SHeiko Schocher  * parse_mtd_partitions - parse MTD partitions
754ff94bc40SHeiko Schocher  * @master: the master partition (describes whole MTD device)
755ff94bc40SHeiko Schocher  * @types: names of partition parsers to try or %NULL
756ff94bc40SHeiko Schocher  * @pparts: array of partitions found is returned here
757ff94bc40SHeiko Schocher  * @data: MTD partition parser-specific data
758ff94bc40SHeiko Schocher  *
759ff94bc40SHeiko Schocher  * This function tries to find partition on MTD device @master. It uses MTD
760ff94bc40SHeiko Schocher  * partition parsers, specified in @types. However, if @types is %NULL, then
761ff94bc40SHeiko Schocher  * the default list of parsers is used. The default list contains only the
762ff94bc40SHeiko Schocher  * "cmdlinepart" and "ofpart" parsers ATM.
763ff94bc40SHeiko Schocher  * Note: If there are more then one parser in @types, the kernel only takes the
764ff94bc40SHeiko Schocher  * partitions parsed out by the first parser.
765ff94bc40SHeiko Schocher  *
766ff94bc40SHeiko Schocher  * This function may return:
767ff94bc40SHeiko Schocher  * o a negative error code in case of failure
768ff94bc40SHeiko Schocher  * o zero if no partitions were found
769ff94bc40SHeiko Schocher  * o a positive number of found partitions, in which case on exit @pparts will
770ff94bc40SHeiko Schocher  *   point to an array containing this number of &struct mtd_info objects.
771ff94bc40SHeiko Schocher  */
772ff94bc40SHeiko Schocher int parse_mtd_partitions(struct mtd_info *master, const char *const *types,
773ff94bc40SHeiko Schocher 			 struct mtd_partition **pparts,
774ff94bc40SHeiko Schocher 			 struct mtd_part_parser_data *data)
775ff94bc40SHeiko Schocher {
776ff94bc40SHeiko Schocher 	struct mtd_part_parser *parser;
777ff94bc40SHeiko Schocher 	int ret = 0;
778ff94bc40SHeiko Schocher 
779ff94bc40SHeiko Schocher 	if (!types)
780ff94bc40SHeiko Schocher 		types = default_mtd_part_types;
781ff94bc40SHeiko Schocher 
782ff94bc40SHeiko Schocher 	for ( ; ret <= 0 && *types; types++) {
783ff94bc40SHeiko Schocher 		parser = get_partition_parser(*types);
784ff94bc40SHeiko Schocher 		if (!parser && !request_module("%s", *types))
785ff94bc40SHeiko Schocher 			parser = get_partition_parser(*types);
786ff94bc40SHeiko Schocher 		if (!parser)
787ff94bc40SHeiko Schocher 			continue;
788ff94bc40SHeiko Schocher 		ret = (*parser->parse_fn)(master, pparts, data);
789ff94bc40SHeiko Schocher 		put_partition_parser(parser);
790ff94bc40SHeiko Schocher 		if (ret > 0) {
791ff94bc40SHeiko Schocher 			printk(KERN_NOTICE "%d %s partitions found on MTD device %s\n",
792ff94bc40SHeiko Schocher 			       ret, parser->name, master->name);
793ff94bc40SHeiko Schocher 			break;
794ff94bc40SHeiko Schocher 		}
795ff94bc40SHeiko Schocher 	}
796ff94bc40SHeiko Schocher 	return ret;
797ff94bc40SHeiko Schocher }
798ff94bc40SHeiko Schocher #endif
799ff94bc40SHeiko Schocher 
800ff94bc40SHeiko Schocher int mtd_is_partition(const struct mtd_info *mtd)
801ff94bc40SHeiko Schocher {
802ff94bc40SHeiko Schocher 	struct mtd_part *part;
803ff94bc40SHeiko Schocher 	int ispart = 0;
804ff94bc40SHeiko Schocher 
805ff94bc40SHeiko Schocher 	mutex_lock(&mtd_partitions_mutex);
806ff94bc40SHeiko Schocher 	list_for_each_entry(part, &mtd_partitions, list)
807ff94bc40SHeiko Schocher 		if (&part->mtd == mtd) {
808ff94bc40SHeiko Schocher 			ispart = 1;
809ff94bc40SHeiko Schocher 			break;
810ff94bc40SHeiko Schocher 		}
811ff94bc40SHeiko Schocher 	mutex_unlock(&mtd_partitions_mutex);
812ff94bc40SHeiko Schocher 
813ff94bc40SHeiko Schocher 	return ispart;
814ff94bc40SHeiko Schocher }
815ff94bc40SHeiko Schocher EXPORT_SYMBOL_GPL(mtd_is_partition);
816ff94bc40SHeiko Schocher 
817ff94bc40SHeiko Schocher /* Returns the size of the entire flash chip */
818ff94bc40SHeiko Schocher uint64_t mtd_get_device_size(const struct mtd_info *mtd)
819ff94bc40SHeiko Schocher {
820ff94bc40SHeiko Schocher 	if (!mtd_is_partition(mtd))
821ff94bc40SHeiko Schocher 		return mtd->size;
822ff94bc40SHeiko Schocher 
823ff94bc40SHeiko Schocher 	return PART(mtd)->master->size;
824ff94bc40SHeiko Schocher }
825ff94bc40SHeiko Schocher EXPORT_SYMBOL_GPL(mtd_get_device_size);
826