mtdpart.c 20.9 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3
/*
 * Simple MTD partitioning layer
 *
D
David Woodhouse 已提交
4 5 6
 * Copyright © 2000 Nicolas Pitre <nico@fluxnic.net>
 * Copyright © 2002 Thomas Gleixner <gleixner@linutronix.de>
 * Copyright © 2000-2010 David Woodhouse <dwmw2@infradead.org>
L
Linus Torvalds 已提交
7
 *
D
David Woodhouse 已提交
8 9 10 11 12 13 14 15 16 17 18 19 20
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
L
Linus Torvalds 已提交
21
 *
22
 */
L
Linus Torvalds 已提交
23 24 25 26 27 28 29 30 31

#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/list.h>
#include <linux/kmod.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
32
#include <linux/err.h>
L
Linus Torvalds 已提交
33

34 35
#include "mtdcore.h"

L
Linus Torvalds 已提交
36 37
/* Our partition linked list */
static LIST_HEAD(mtd_partitions);
38
static DEFINE_MUTEX(mtd_partitions_mutex);
L
Linus Torvalds 已提交
39 40 41 42 43

/* Our partition node structure */
struct mtd_part {
	struct mtd_info mtd;
	struct mtd_info *master;
44
	uint64_t offset;
L
Linus Torvalds 已提交
45 46 47 48 49 50 51 52 53
	struct list_head list;
};

/*
 * Given a pointer to the MTD object in the mtd_part structure, we can retrieve
 * the pointer to that structure with this macro.
 */
#define PART(x)  ((struct mtd_part *)(x))

54 55

/*
L
Linus Torvalds 已提交
56 57 58 59
 * MTD methods which simply translate the effective address and pass through
 * to the _real_ device.
 */

60 61
static int part_read(struct mtd_info *mtd, loff_t from, size_t len,
		size_t *retlen, u_char *buf)
L
Linus Torvalds 已提交
62 63
{
	struct mtd_part *part = PART(mtd);
64
	struct mtd_ecc_stats stats;
65 66
	int res;

67
	stats = part->master->ecc_stats;
M
Mike Dunn 已提交
68 69
	res = part->master->_read(part->master, from + part->offset, len,
				  retlen, buf);
70 71 72 73 74 75
	if (unlikely(mtd_is_eccerr(res)))
		mtd->ecc_stats.failed +=
			part->master->ecc_stats.failed - stats.failed;
	else
		mtd->ecc_stats.corrected +=
			part->master->ecc_stats.corrected - stats.corrected;
76
	return res;
L
Linus Torvalds 已提交
77 78
}

79 80
static int part_point(struct mtd_info *mtd, loff_t from, size_t len,
		size_t *retlen, void **virt, resource_size_t *phys)
L
Linus Torvalds 已提交
81 82
{
	struct mtd_part *part = PART(mtd);
83

M
Mike Dunn 已提交
84 85
	return part->master->_point(part->master, from + part->offset, len,
				    retlen, virt, phys);
L
Linus Torvalds 已提交
86
}
87

88
static int part_unpoint(struct mtd_info *mtd, loff_t from, size_t len)
L
Linus Torvalds 已提交
89 90 91
{
	struct mtd_part *part = PART(mtd);

M
Mike Dunn 已提交
92
	return part->master->_unpoint(part->master, from + part->offset, len);
L
Linus Torvalds 已提交
93 94
}

95 96 97 98 99 100 101 102
static unsigned long part_get_unmapped_area(struct mtd_info *mtd,
					    unsigned long len,
					    unsigned long offset,
					    unsigned long flags)
{
	struct mtd_part *part = PART(mtd);

	offset += part->offset;
M
Mike Dunn 已提交
103 104
	return part->master->_get_unmapped_area(part->master, len, offset,
						flags);
105 106
}

107
static int part_read_oob(struct mtd_info *mtd, loff_t from,
108
		struct mtd_oob_ops *ops)
L
Linus Torvalds 已提交
109 110
{
	struct mtd_part *part = PART(mtd);
111
	int res;
112

L
Linus Torvalds 已提交
113
	if (from >= mtd->size)
114
		return -EINVAL;
115
	if (ops->datbuf && from + ops->len > mtd->size)
116
		return -EINVAL;
117

118 119 120 121 122 123 124
	/*
	 * If OOB is also requested, make sure that we do not read past the end
	 * of this partition.
	 */
	if (ops->oobbuf) {
		size_t len, pages;

125
		if (ops->mode == MTD_OPS_AUTO_OOB)
126 127 128 129 130 131 132 133 134
			len = mtd->oobavail;
		else
			len = mtd->oobsize;
		pages = mtd_div_by_ws(mtd->size, mtd);
		pages -= mtd_div_by_ws(from, mtd);
		if (ops->ooboffs + ops->ooblen > pages * len)
			return -EINVAL;
	}

M
Mike Dunn 已提交
135
	res = part->master->_read_oob(part->master, from + part->offset, ops);
136
	if (unlikely(res)) {
137
		if (mtd_is_bitflip(res))
138
			mtd->ecc_stats.corrected++;
139
		if (mtd_is_eccerr(res))
140 141 142
			mtd->ecc_stats.failed++;
	}
	return res;
L
Linus Torvalds 已提交
143 144
}

145 146
static int part_read_user_prot_reg(struct mtd_info *mtd, loff_t from,
		size_t len, size_t *retlen, u_char *buf)
L
Linus Torvalds 已提交
147 148
{
	struct mtd_part *part = PART(mtd);
M
Mike Dunn 已提交
149 150
	return part->master->_read_user_prot_reg(part->master, from, len,
						 retlen, buf);
L
Linus Torvalds 已提交
151 152
}

153 154
static int part_get_user_prot_info(struct mtd_info *mtd,
		struct otp_info *buf, size_t len)
155 156
{
	struct mtd_part *part = PART(mtd);
M
Mike Dunn 已提交
157
	return part->master->_get_user_prot_info(part->master, buf, len);
158 159
}

160 161
static int part_read_fact_prot_reg(struct mtd_info *mtd, loff_t from,
		size_t len, size_t *retlen, u_char *buf)
L
Linus Torvalds 已提交
162 163
{
	struct mtd_part *part = PART(mtd);
M
Mike Dunn 已提交
164 165
	return part->master->_read_fact_prot_reg(part->master, from, len,
						 retlen, buf);
L
Linus Torvalds 已提交
166 167
}

168 169
static int part_get_fact_prot_info(struct mtd_info *mtd, struct otp_info *buf,
		size_t len)
170 171
{
	struct mtd_part *part = PART(mtd);
M
Mike Dunn 已提交
172
	return part->master->_get_fact_prot_info(part->master, buf, len);
173 174
}

175 176
static int part_write(struct mtd_info *mtd, loff_t to, size_t len,
		size_t *retlen, const u_char *buf)
L
Linus Torvalds 已提交
177 178
{
	struct mtd_part *part = PART(mtd);
M
Mike Dunn 已提交
179 180
	return part->master->_write(part->master, to + part->offset, len,
				    retlen, buf);
L
Linus Torvalds 已提交
181 182
}

183 184
static int part_panic_write(struct mtd_info *mtd, loff_t to, size_t len,
		size_t *retlen, const u_char *buf)
185 186
{
	struct mtd_part *part = PART(mtd);
M
Mike Dunn 已提交
187 188
	return part->master->_panic_write(part->master, to + part->offset, len,
					  retlen, buf);
189 190
}

191
static int part_write_oob(struct mtd_info *mtd, loff_t to,
192
		struct mtd_oob_ops *ops)
L
Linus Torvalds 已提交
193 194
{
	struct mtd_part *part = PART(mtd);
195

L
Linus Torvalds 已提交
196
	if (to >= mtd->size)
197
		return -EINVAL;
198
	if (ops->datbuf && to + ops->len > mtd->size)
199
		return -EINVAL;
M
Mike Dunn 已提交
200
	return part->master->_write_oob(part->master, to + part->offset, ops);
L
Linus Torvalds 已提交
201 202
}

203 204
static int part_write_user_prot_reg(struct mtd_info *mtd, loff_t from,
		size_t len, size_t *retlen, u_char *buf)
L
Linus Torvalds 已提交
205 206
{
	struct mtd_part *part = PART(mtd);
M
Mike Dunn 已提交
207 208
	return part->master->_write_user_prot_reg(part->master, from, len,
						  retlen, buf);
L
Linus Torvalds 已提交
209 210
}

211 212
static int part_lock_user_prot_reg(struct mtd_info *mtd, loff_t from,
		size_t len)
213 214
{
	struct mtd_part *part = PART(mtd);
M
Mike Dunn 已提交
215
	return part->master->_lock_user_prot_reg(part->master, from, len);
216 217
}

218 219
static int part_writev(struct mtd_info *mtd, const struct kvec *vecs,
		unsigned long count, loff_t to, size_t *retlen)
L
Linus Torvalds 已提交
220 221
{
	struct mtd_part *part = PART(mtd);
M
Mike Dunn 已提交
222 223
	return part->master->_writev(part->master, vecs, count,
				     to + part->offset, retlen);
L
Linus Torvalds 已提交
224 225
}

226
static int part_erase(struct mtd_info *mtd, struct erase_info *instr)
L
Linus Torvalds 已提交
227 228 229
{
	struct mtd_part *part = PART(mtd);
	int ret;
230

L
Linus Torvalds 已提交
231
	instr->addr += part->offset;
M
Mike Dunn 已提交
232
	ret = part->master->_erase(part->master, instr);
233
	if (ret) {
234
		if (instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN)
235 236 237
			instr->fail_addr -= part->offset;
		instr->addr -= part->offset;
	}
L
Linus Torvalds 已提交
238 239 240 241 242
	return ret;
}

void mtd_erase_callback(struct erase_info *instr)
{
243
	if (instr->mtd->_erase == part_erase) {
L
Linus Torvalds 已提交
244 245
		struct mtd_part *part = PART(instr->mtd);

246
		if (instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN)
L
Linus Torvalds 已提交
247 248 249 250 251 252 253 254
			instr->fail_addr -= part->offset;
		instr->addr -= part->offset;
	}
	if (instr->callback)
		instr->callback(instr);
}
EXPORT_SYMBOL_GPL(mtd_erase_callback);

255
static int part_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
L
Linus Torvalds 已提交
256 257
{
	struct mtd_part *part = PART(mtd);
M
Mike Dunn 已提交
258
	return part->master->_lock(part->master, ofs + part->offset, len);
L
Linus Torvalds 已提交
259 260
}

261
static int part_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
L
Linus Torvalds 已提交
262 263
{
	struct mtd_part *part = PART(mtd);
M
Mike Dunn 已提交
264
	return part->master->_unlock(part->master, ofs + part->offset, len);
L
Linus Torvalds 已提交
265 266
}

267 268 269
static int part_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len)
{
	struct mtd_part *part = PART(mtd);
M
Mike Dunn 已提交
270
	return part->master->_is_locked(part->master, ofs + part->offset, len);
271 272
}

L
Linus Torvalds 已提交
273 274 275
static void part_sync(struct mtd_info *mtd)
{
	struct mtd_part *part = PART(mtd);
M
Mike Dunn 已提交
276
	part->master->_sync(part->master);
L
Linus Torvalds 已提交
277 278 279 280 281
}

static int part_suspend(struct mtd_info *mtd)
{
	struct mtd_part *part = PART(mtd);
M
Mike Dunn 已提交
282
	return part->master->_suspend(part->master);
L
Linus Torvalds 已提交
283 284 285 286 287
}

static void part_resume(struct mtd_info *mtd)
{
	struct mtd_part *part = PART(mtd);
M
Mike Dunn 已提交
288
	part->master->_resume(part->master);
L
Linus Torvalds 已提交
289 290
}

291
static int part_block_isbad(struct mtd_info *mtd, loff_t ofs)
L
Linus Torvalds 已提交
292 293 294
{
	struct mtd_part *part = PART(mtd);
	ofs += part->offset;
M
Mike Dunn 已提交
295
	return part->master->_block_isbad(part->master, ofs);
L
Linus Torvalds 已提交
296 297
}

298
static int part_block_markbad(struct mtd_info *mtd, loff_t ofs)
L
Linus Torvalds 已提交
299 300
{
	struct mtd_part *part = PART(mtd);
301 302
	int res;

L
Linus Torvalds 已提交
303
	ofs += part->offset;
M
Mike Dunn 已提交
304
	res = part->master->_block_markbad(part->master, ofs);
305 306 307
	if (!res)
		mtd->ecc_stats.badblocks++;
	return res;
L
Linus Torvalds 已提交
308 309
}

310 311 312 313 314 315
static inline void free_partition(struct mtd_part *p)
{
	kfree(p->mtd.name);
	kfree(p);
}

316 317
/*
 * This function unregisters and destroy all slave MTD objects which are
L
Linus Torvalds 已提交
318 319 320 321 322
 * attached to the given master MTD object.
 */

int del_mtd_partitions(struct mtd_info *master)
{
323
	struct mtd_part *slave, *next;
324
	int ret, err = 0;
L
Linus Torvalds 已提交
325

326
	mutex_lock(&mtd_partitions_mutex);
327
	list_for_each_entry_safe(slave, next, &mtd_partitions, list)
L
Linus Torvalds 已提交
328
		if (slave->master == master) {
329 330 331 332 333
			ret = del_mtd_device(&slave->mtd);
			if (ret < 0) {
				err = ret;
				continue;
			}
334
			list_del(&slave->list);
335
			free_partition(slave);
L
Linus Torvalds 已提交
336
		}
337
	mutex_unlock(&mtd_partitions_mutex);
L
Linus Torvalds 已提交
338

339
	return err;
L
Linus Torvalds 已提交
340 341
}

342 343 344
static struct mtd_part *allocate_partition(struct mtd_info *master,
			const struct mtd_partition *part, int partno,
			uint64_t cur_offset)
345 346
{
	struct mtd_part *slave;
347
	char *name;
348 349

	/* allocate the partition structure */
350
	slave = kzalloc(sizeof(*slave), GFP_KERNEL);
351 352
	name = kstrdup(part->name, GFP_KERNEL);
	if (!name || !slave) {
353
		printk(KERN_ERR"memory allocation error while creating partitions for \"%s\"\n",
354 355 356 357
		       master->name);
		kfree(name);
		kfree(slave);
		return ERR_PTR(-ENOMEM);
358 359 360 361 362 363 364
	}

	/* set up the MTD object for this partition */
	slave->mtd.type = master->type;
	slave->mtd.flags = master->flags & ~part->mask_flags;
	slave->mtd.size = part->size;
	slave->mtd.writesize = master->writesize;
365
	slave->mtd.writebufsize = master->writebufsize;
366 367 368 369
	slave->mtd.oobsize = master->oobsize;
	slave->mtd.oobavail = master->oobavail;
	slave->mtd.subpage_sft = master->subpage_sft;

370
	slave->mtd.name = name;
371
	slave->mtd.owner = master->owner;
372
	slave->mtd.backing_dev_info = master->backing_dev_info;
373

D
David Brownell 已提交
374 375 376 377 378
	/* NOTE:  we don't arrange MTDs as a tree; it'd be error-prone
	 * to have the same data be in two different partitions.
	 */
	slave->mtd.dev.parent = master->dev.parent;

379 380
	slave->mtd._read = part_read;
	slave->mtd._write = part_write;
381

382 383
	if (master->_panic_write)
		slave->mtd._panic_write = part_panic_write;
384

385 386 387
	if (master->_point && master->_unpoint) {
		slave->mtd._point = part_point;
		slave->mtd._unpoint = part_unpoint;
388 389
	}

390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413
	if (master->_get_unmapped_area)
		slave->mtd._get_unmapped_area = part_get_unmapped_area;
	if (master->_read_oob)
		slave->mtd._read_oob = part_read_oob;
	if (master->_write_oob)
		slave->mtd._write_oob = part_write_oob;
	if (master->_read_user_prot_reg)
		slave->mtd._read_user_prot_reg = part_read_user_prot_reg;
	if (master->_read_fact_prot_reg)
		slave->mtd._read_fact_prot_reg = part_read_fact_prot_reg;
	if (master->_write_user_prot_reg)
		slave->mtd._write_user_prot_reg = part_write_user_prot_reg;
	if (master->_lock_user_prot_reg)
		slave->mtd._lock_user_prot_reg = part_lock_user_prot_reg;
	if (master->_get_user_prot_info)
		slave->mtd._get_user_prot_info = part_get_user_prot_info;
	if (master->_get_fact_prot_info)
		slave->mtd._get_fact_prot_info = part_get_fact_prot_info;
	if (master->_sync)
		slave->mtd._sync = part_sync;
	if (!partno && !master->dev.class && master->_suspend &&
	    master->_resume) {
			slave->mtd._suspend = part_suspend;
			slave->mtd._resume = part_resume;
414
	}
415 416 417 418 419 420 421 422 423 424 425 426 427
	if (master->_writev)
		slave->mtd._writev = part_writev;
	if (master->_lock)
		slave->mtd._lock = part_lock;
	if (master->_unlock)
		slave->mtd._unlock = part_unlock;
	if (master->_is_locked)
		slave->mtd._is_locked = part_is_locked;
	if (master->_block_isbad)
		slave->mtd._block_isbad = part_block_isbad;
	if (master->_block_markbad)
		slave->mtd._block_markbad = part_block_markbad;
	slave->mtd._erase = part_erase;
428 429 430 431 432 433 434
	slave->master = master;
	slave->offset = part->offset;

	if (slave->offset == MTDPART_OFS_APPEND)
		slave->offset = cur_offset;
	if (slave->offset == MTDPART_OFS_NXTBLK) {
		slave->offset = cur_offset;
435
		if (mtd_mod_by_eb(cur_offset, master) != 0) {
436
			/* Round up to next erasesize */
437
			slave->offset = (mtd_div_by_eb(cur_offset, master) + 1) * master->erasesize;
438
			printk(KERN_NOTICE "Moving partition %d: "
439 440
			       "0x%012llx -> 0x%012llx\n", partno,
			       (unsigned long long)cur_offset, (unsigned long long)slave->offset);
441 442
		}
	}
443 444 445 446 447 448 449 450 451 452 453 454 455
	if (slave->offset == MTDPART_OFS_RETAIN) {
		slave->offset = cur_offset;
		if (master->size - slave->offset >= slave->mtd.size) {
			slave->mtd.size = master->size - slave->offset
							- slave->mtd.size;
		} else {
			printk(KERN_ERR "mtd partition \"%s\" doesn't have enough space: %#llx < %#llx, disabled\n",
				part->name, master->size - slave->offset,
				slave->mtd.size);
			/* register to preserve ordering */
			goto out_register;
		}
	}
456 457 458
	if (slave->mtd.size == MTDPART_SIZ_FULL)
		slave->mtd.size = master->size - slave->offset;

459 460
	printk(KERN_NOTICE "0x%012llx-0x%012llx : \"%s\"\n", (unsigned long long)slave->offset,
		(unsigned long long)(slave->offset + slave->mtd.size), slave->mtd.name);
461 462 463

	/* let's do some sanity checks */
	if (slave->offset >= master->size) {
464
		/* let's register it anyway to preserve ordering */
465 466
		slave->offset = 0;
		slave->mtd.size = 0;
467
		printk(KERN_ERR"mtd: partition \"%s\" is out of reach -- disabled\n",
468
			part->name);
469
		goto out_register;
470 471 472
	}
	if (slave->offset + slave->mtd.size > master->size) {
		slave->mtd.size = master->size - slave->offset;
473 474
		printk(KERN_WARNING"mtd: partition \"%s\" extends beyond the end of device \"%s\" -- size truncated to %#llx\n",
			part->name, master->name, (unsigned long long)slave->mtd.size);
475
	}
476
	if (master->numeraseregions > 1) {
477
		/* Deal with variable erase size stuff */
478
		int i, max = master->numeraseregions;
479
		u64 end = slave->offset + slave->mtd.size;
480 481
		struct mtd_erase_region_info *regions = master->eraseregions;

482 483 484
		/* Find the first erase regions which is part of this
		 * partition. */
		for (i = 0; i < max && regions[i].offset <= slave->offset; i++)
485
			;
486
		/* The loop searched for the region _behind_ the first one */
487 488
		if (i > 0)
			i--;
489

490 491
		/* Pick biggest erasesize */
		for (; i < max && regions[i].offset < end; i++) {
492 493 494 495
			if (slave->mtd.erasesize < regions[i].erasesize) {
				slave->mtd.erasesize = regions[i].erasesize;
			}
		}
496
		BUG_ON(slave->mtd.erasesize == 0);
497 498 499 500 501 502
	} else {
		/* Single erase size */
		slave->mtd.erasesize = master->erasesize;
	}

	if ((slave->mtd.flags & MTD_WRITEABLE) &&
503
	    mtd_mod_by_eb(slave->offset, &slave->mtd)) {
504
		/* Doesn't start on a boundary of major erase size */
505 506
		/* FIXME: Let it be writable if it is on a boundary of
		 * _minor_ erase size though */
507
		slave->mtd.flags &= ~MTD_WRITEABLE;
508
		printk(KERN_WARNING"mtd: partition \"%s\" doesn't start on an erase block boundary -- force read-only\n",
509 510 511
			part->name);
	}
	if ((slave->mtd.flags & MTD_WRITEABLE) &&
512
	    mtd_mod_by_eb(slave->mtd.size, &slave->mtd)) {
513
		slave->mtd.flags &= ~MTD_WRITEABLE;
514
		printk(KERN_WARNING"mtd: partition \"%s\" doesn't end on an erase block -- force read-only\n",
515 516 517 518
			part->name);
	}

	slave->mtd.ecclayout = master->ecclayout;
519
	slave->mtd.ecc_step_size = master->ecc_step_size;
M
Mike Dunn 已提交
520
	slave->mtd.ecc_strength = master->ecc_strength;
521 522
	slave->mtd.bitflip_threshold = master->bitflip_threshold;

523
	if (master->_block_isbad) {
524
		uint64_t offs = 0;
525

526
		while (offs < slave->mtd.size) {
527
			if (mtd_block_isbad(master, offs + slave->offset))
528 529 530 531 532
				slave->mtd.ecc_stats.badblocks++;
			offs += slave->mtd.erasesize;
		}
	}

533
out_register:
534 535 536
	return slave;
}

537
int mtd_add_partition(struct mtd_info *master, const char *name,
538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616
		      long long offset, long long length)
{
	struct mtd_partition part;
	struct mtd_part *p, *new;
	uint64_t start, end;
	int ret = 0;

	/* the direct offset is expected */
	if (offset == MTDPART_OFS_APPEND ||
	    offset == MTDPART_OFS_NXTBLK)
		return -EINVAL;

	if (length == MTDPART_SIZ_FULL)
		length = master->size - offset;

	if (length <= 0)
		return -EINVAL;

	part.name = name;
	part.size = length;
	part.offset = offset;
	part.mask_flags = 0;
	part.ecclayout = NULL;

	new = allocate_partition(master, &part, -1, offset);
	if (IS_ERR(new))
		return PTR_ERR(new);

	start = offset;
	end = offset + length;

	mutex_lock(&mtd_partitions_mutex);
	list_for_each_entry(p, &mtd_partitions, list)
		if (p->master == master) {
			if ((start >= p->offset) &&
			    (start < (p->offset + p->mtd.size)))
				goto err_inv;

			if ((end >= p->offset) &&
			    (end < (p->offset + p->mtd.size)))
				goto err_inv;
		}

	list_add(&new->list, &mtd_partitions);
	mutex_unlock(&mtd_partitions_mutex);

	add_mtd_device(&new->mtd);

	return ret;
err_inv:
	mutex_unlock(&mtd_partitions_mutex);
	free_partition(new);
	return -EINVAL;
}
EXPORT_SYMBOL_GPL(mtd_add_partition);

int mtd_del_partition(struct mtd_info *master, int partno)
{
	struct mtd_part *slave, *next;
	int ret = -EINVAL;

	mutex_lock(&mtd_partitions_mutex);
	list_for_each_entry_safe(slave, next, &mtd_partitions, list)
		if ((slave->master == master) &&
		    (slave->mtd.index == partno)) {
			ret = del_mtd_device(&slave->mtd);
			if (ret < 0)
				break;

			list_del(&slave->list);
			free_partition(slave);
			break;
		}
	mutex_unlock(&mtd_partitions_mutex);

	return ret;
}
EXPORT_SYMBOL_GPL(mtd_del_partition);

L
Linus Torvalds 已提交
617 618 619 620
/*
 * This function, given a master MTD object and a partition table, creates
 * and registers slave MTD objects which are bound to the master according to
 * the partition definitions.
D
David Brownell 已提交
621 622 623
 *
 * We don't register the master, or expect the caller to have done so,
 * for reasons of data integrity.
L
Linus Torvalds 已提交
624 625
 */

626
int add_mtd_partitions(struct mtd_info *master,
L
Linus Torvalds 已提交
627 628 629 630
		       const struct mtd_partition *parts,
		       int nbparts)
{
	struct mtd_part *slave;
631
	uint64_t cur_offset = 0;
L
Linus Torvalds 已提交
632 633
	int i;

634
	printk(KERN_NOTICE "Creating %d MTD partitions on \"%s\":\n", nbparts, master->name);
L
Linus Torvalds 已提交
635 636

	for (i = 0; i < nbparts; i++) {
637 638 639 640 641 642 643 644 645 646
		slave = allocate_partition(master, parts + i, i, cur_offset);
		if (IS_ERR(slave))
			return PTR_ERR(slave);

		mutex_lock(&mtd_partitions_mutex);
		list_add(&slave->list, &mtd_partitions);
		mutex_unlock(&mtd_partitions_mutex);

		add_mtd_device(&slave->mtd);

L
Linus Torvalds 已提交
647 648 649 650 651 652 653 654 655 656 657
		cur_offset = slave->offset + slave->mtd.size;
	}

	return 0;
}

static DEFINE_SPINLOCK(part_parser_lock);
static LIST_HEAD(part_parsers);

static struct mtd_part_parser *get_partition_parser(const char *name)
{
658
	struct mtd_part_parser *p, *ret = NULL;
L
Linus Torvalds 已提交
659

660
	spin_lock(&part_parser_lock);
L
Linus Torvalds 已提交
661

662
	list_for_each_entry(p, &part_parsers, list)
L
Linus Torvalds 已提交
663 664 665 666
		if (!strcmp(p->name, name) && try_module_get(p->owner)) {
			ret = p;
			break;
		}
667

L
Linus Torvalds 已提交
668 669 670 671 672
	spin_unlock(&part_parser_lock);

	return ret;
}

673 674
#define put_partition_parser(p) do { module_put((p)->owner); } while (0)

675
void register_mtd_parser(struct mtd_part_parser *p)
L
Linus Torvalds 已提交
676 677 678 679 680
{
	spin_lock(&part_parser_lock);
	list_add(&p->list, &part_parsers);
	spin_unlock(&part_parser_lock);
}
681
EXPORT_SYMBOL_GPL(register_mtd_parser);
L
Linus Torvalds 已提交
682

683
void deregister_mtd_parser(struct mtd_part_parser *p)
L
Linus Torvalds 已提交
684 685 686 687 688
{
	spin_lock(&part_parser_lock);
	list_del(&p->list);
	spin_unlock(&part_parser_lock);
}
689
EXPORT_SYMBOL_GPL(deregister_mtd_parser);
L
Linus Torvalds 已提交
690

691 692 693 694
/*
 * Do not forget to update 'parse_mtd_partitions()' kerneldoc comment if you
 * are changing this array!
 */
695
static const char * const default_mtd_part_types[] = {
696 697 698 699
	"cmdlinepart",
	"ofpart",
	NULL
};
700

701 702 703 704 705
/**
 * parse_mtd_partitions - parse MTD partitions
 * @master: the master partition (describes whole MTD device)
 * @types: names of partition parsers to try or %NULL
 * @pparts: array of partitions found is returned here
706
 * @data: MTD partition parser-specific data
707 708 709 710
 *
 * This function tries to find partition on MTD device @master. It uses MTD
 * partition parsers, specified in @types. However, if @types is %NULL, then
 * the default list of parsers is used. The default list contains only the
711
 * "cmdlinepart" and "ofpart" parsers ATM.
712 713
 * Note: If there are more then one parser in @types, the kernel only takes the
 * partitions parsed out by the first parser.
714 715 716 717 718 719 720
 *
 * This function may return:
 * o a negative error code in case of failure
 * o zero if no partitions were found
 * o a positive number of found partitions, in which case on exit @pparts will
 *   point to an array containing this number of &struct mtd_info objects.
 */
721
int parse_mtd_partitions(struct mtd_info *master, const char *const *types,
722 723
			 struct mtd_partition **pparts,
			 struct mtd_part_parser_data *data)
L
Linus Torvalds 已提交
724 725 726
{
	struct mtd_part_parser *parser;
	int ret = 0;
727

728 729 730
	if (!types)
		types = default_mtd_part_types;

L
Linus Torvalds 已提交
731 732 733
	for ( ; ret <= 0 && *types; types++) {
		parser = get_partition_parser(*types);
		if (!parser && !request_module("%s", *types))
734
			parser = get_partition_parser(*types);
735
		if (!parser)
L
Linus Torvalds 已提交
736
			continue;
737
		ret = (*parser->parse_fn)(master, pparts, data);
738
		put_partition_parser(parser);
L
Linus Torvalds 已提交
739
		if (ret > 0) {
740
			printk(KERN_NOTICE "%d %s partitions found on MTD device %s\n",
L
Linus Torvalds 已提交
741
			       ret, parser->name, master->name);
742
			break;
L
Linus Torvalds 已提交
743 744 745 746
		}
	}
	return ret;
}
747

748
int mtd_is_partition(const struct mtd_info *mtd)
749 750
{
	struct mtd_part *part;
751
	int ispart = 0;
752 753 754 755

	mutex_lock(&mtd_partitions_mutex);
	list_for_each_entry(part, &mtd_partitions, list)
		if (&part->mtd == mtd) {
756
			ispart = 1;
757 758 759 760
			break;
		}
	mutex_unlock(&mtd_partitions_mutex);

761
	return ispart;
762
}
763
EXPORT_SYMBOL_GPL(mtd_is_partition);
764 765 766 767 768 769 770 771 772 773

/* Returns the size of the entire flash chip */
uint64_t mtd_get_device_size(const struct mtd_info *mtd)
{
	if (!mtd_is_partition(mtd))
		return mtd->size;

	return PART(mtd)->master->size;
}
EXPORT_SYMBOL_GPL(mtd_get_device_size);