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