Blame view

drivers/mtd/mtdpart.c 19.8 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
  /*
   * Simple MTD partitioning layer
   *
a1452a377   David Woodhouse   mtd: Update copyr...
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>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
7
   *
a1452a377   David Woodhouse   mtd: Update copyr...
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
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
21
   *
97894cda5   Thomas Gleixner   [MTD] core: Clean...
22
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
23
24
25
26
27
28
  
  #include <linux/module.h>
  #include <linux/types.h>
  #include <linux/kernel.h>
  #include <linux/slab.h>
  #include <linux/list.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
29
30
31
  #include <linux/kmod.h>
  #include <linux/mtd/mtd.h>
  #include <linux/mtd/partitions.h>
5daa7b214   Roman Tereshonkov   mtd: prepare part...
32
  #include <linux/err.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
33

eea72d5fd   Jamie Iles   mtd: remove add_m...
34
  #include "mtdcore.h"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
35
36
  /* Our partition linked list */
  static LIST_HEAD(mtd_partitions);
5daa7b214   Roman Tereshonkov   mtd: prepare part...
37
  static DEFINE_MUTEX(mtd_partitions_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
38
39
40
41
42
  
  /* Our partition node structure */
  struct mtd_part {
  	struct mtd_info mtd;
  	struct mtd_info *master;
69423d99f   Adrian Hunter   [MTD] update inte...
43
  	uint64_t offset;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
44
  	struct list_head list;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
45
46
47
48
49
50
51
  };
  
  /*
   * 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))
97894cda5   Thomas Gleixner   [MTD] core: Clean...
52
53
  
  /*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
54
55
56
   * MTD methods which simply translate the effective address and pass through
   * to the _real_ device.
   */
b33a28873   Atsushi Nemoto   [MTD][MTDPART] Ha...
57
58
  static int part_read(struct mtd_info *mtd, loff_t from, size_t len,
  		size_t *retlen, u_char *buf)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
59
60
  {
  	struct mtd_part *part = PART(mtd);
d8877f191   Yauhen Kharuzhy   [MTD] mtdpart: Ma...
61
  	struct mtd_ecc_stats stats;
f1a28c028   Thomas Gleixner   [MTD] NAND Expose...
62
  	int res;
d8877f191   Yauhen Kharuzhy   [MTD] mtdpart: Ma...
63
  	stats = part->master->ecc_stats;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
64
65
66
67
  	if (from >= mtd->size)
  		len = 0;
  	else if (from + len > mtd->size)
  		len = mtd->size - from;
b33a28873   Atsushi Nemoto   [MTD][MTDPART] Ha...
68
  	res = part->master->read(part->master, from + part->offset,
9223a456d   Thomas Gleixner   [MTD] Remove read...
69
  				   len, retlen, buf);
f1a28c028   Thomas Gleixner   [MTD] NAND Expose...
70
71
  	if (unlikely(res)) {
  		if (res == -EUCLEAN)
d8877f191   Yauhen Kharuzhy   [MTD] mtdpart: Ma...
72
  			mtd->ecc_stats.corrected += part->master->ecc_stats.corrected - stats.corrected;
f1a28c028   Thomas Gleixner   [MTD] NAND Expose...
73
  		if (res == -EBADMSG)
d8877f191   Yauhen Kharuzhy   [MTD] mtdpart: Ma...
74
  			mtd->ecc_stats.failed += part->master->ecc_stats.failed - stats.failed;
f1a28c028   Thomas Gleixner   [MTD] NAND Expose...
75
76
  	}
  	return res;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
77
  }
b33a28873   Atsushi Nemoto   [MTD][MTDPART] Ha...
78
79
  static int part_point(struct mtd_info *mtd, loff_t from, size_t len,
  		size_t *retlen, void **virt, resource_size_t *phys)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
80
81
82
83
84
85
  {
  	struct mtd_part *part = PART(mtd);
  	if (from >= mtd->size)
  		len = 0;
  	else if (from + len > mtd->size)
  		len = mtd->size - from;
97894cda5   Thomas Gleixner   [MTD] core: Clean...
86
  	return part->master->point (part->master, from + part->offset,
a98889f3d   Jared Hulbert   [MTD][NOR] Add ph...
87
  				    len, retlen, virt, phys);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
88
  }
9223a456d   Thomas Gleixner   [MTD] Remove read...
89

a98889f3d   Jared Hulbert   [MTD][NOR] Add ph...
90
  static void part_unpoint(struct mtd_info *mtd, loff_t from, size_t len)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
91
92
  {
  	struct mtd_part *part = PART(mtd);
a98889f3d   Jared Hulbert   [MTD][NOR] Add ph...
93
  	part->master->unpoint(part->master, from + part->offset, len);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
94
  }
402d32651   David Howells   NOMMU: Present ba...
95
96
97
98
99
100
101
102
103
104
105
  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;
  	return part->master->get_unmapped_area(part->master, len, offset,
  					       flags);
  }
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
106
  static int part_read_oob(struct mtd_info *mtd, loff_t from,
b33a28873   Atsushi Nemoto   [MTD][MTDPART] Ha...
107
  		struct mtd_oob_ops *ops)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
108
109
  {
  	struct mtd_part *part = PART(mtd);
f1a28c028   Thomas Gleixner   [MTD] NAND Expose...
110
  	int res;
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
111

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
112
  	if (from >= mtd->size)
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
113
  		return -EINVAL;
7014568ba   Vitaly Wool   [MTD] [NAND] remo...
114
  	if (ops->datbuf && from + ops->len > mtd->size)
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
115
  		return -EINVAL;
f1a28c028   Thomas Gleixner   [MTD] NAND Expose...
116

154bf89f5   Artem Bityutskiy   mtd: mtdpart: dis...
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
  	/*
  	 * 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;
  
  		if (ops->mode == MTD_OOB_AUTO)
  			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;
  	}
  
  	res = part->master->read_oob(part->master, from + part->offset, ops);
f1a28c028   Thomas Gleixner   [MTD] NAND Expose...
135
136
137
138
139
140
141
  	if (unlikely(res)) {
  		if (res == -EUCLEAN)
  			mtd->ecc_stats.corrected++;
  		if (res == -EBADMSG)
  			mtd->ecc_stats.failed++;
  	}
  	return res;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
142
  }
b33a28873   Atsushi Nemoto   [MTD][MTDPART] Ha...
143
144
  static int part_read_user_prot_reg(struct mtd_info *mtd, loff_t from,
  		size_t len, size_t *retlen, u_char *buf)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
145
146
  {
  	struct mtd_part *part = PART(mtd);
b33a28873   Atsushi Nemoto   [MTD][MTDPART] Ha...
147
  	return part->master->read_user_prot_reg(part->master, from,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
148
149
  					len, retlen, buf);
  }
b33a28873   Atsushi Nemoto   [MTD][MTDPART] Ha...
150
151
  static int part_get_user_prot_info(struct mtd_info *mtd,
  		struct otp_info *buf, size_t len)
f77814dd5   Nicolas Pitre   [MTD] Support for...
152
153
  {
  	struct mtd_part *part = PART(mtd);
b33a28873   Atsushi Nemoto   [MTD][MTDPART] Ha...
154
  	return part->master->get_user_prot_info(part->master, buf, len);
f77814dd5   Nicolas Pitre   [MTD] Support for...
155
  }
b33a28873   Atsushi Nemoto   [MTD][MTDPART] Ha...
156
157
  static int part_read_fact_prot_reg(struct mtd_info *mtd, loff_t from,
  		size_t len, size_t *retlen, u_char *buf)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
158
159
  {
  	struct mtd_part *part = PART(mtd);
b33a28873   Atsushi Nemoto   [MTD][MTDPART] Ha...
160
  	return part->master->read_fact_prot_reg(part->master, from,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
161
162
  					len, retlen, buf);
  }
b33a28873   Atsushi Nemoto   [MTD][MTDPART] Ha...
163
164
  static int part_get_fact_prot_info(struct mtd_info *mtd, struct otp_info *buf,
  		size_t len)
f77814dd5   Nicolas Pitre   [MTD] Support for...
165
166
  {
  	struct mtd_part *part = PART(mtd);
b33a28873   Atsushi Nemoto   [MTD][MTDPART] Ha...
167
  	return part->master->get_fact_prot_info(part->master, buf, len);
f77814dd5   Nicolas Pitre   [MTD] Support for...
168
  }
b33a28873   Atsushi Nemoto   [MTD][MTDPART] Ha...
169
170
  static int part_write(struct mtd_info *mtd, loff_t to, size_t len,
  		size_t *retlen, const u_char *buf)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
171
172
173
174
175
176
177
178
  {
  	struct mtd_part *part = PART(mtd);
  	if (!(mtd->flags & MTD_WRITEABLE))
  		return -EROFS;
  	if (to >= mtd->size)
  		len = 0;
  	else if (to + len > mtd->size)
  		len = mtd->size - to;
b33a28873   Atsushi Nemoto   [MTD][MTDPART] Ha...
179
  	return part->master->write(part->master, to + part->offset,
9223a456d   Thomas Gleixner   [MTD] Remove read...
180
  				    len, retlen, buf);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
181
  }
b33a28873   Atsushi Nemoto   [MTD][MTDPART] Ha...
182
183
  static int part_panic_write(struct mtd_info *mtd, loff_t to, size_t len,
  		size_t *retlen, const u_char *buf)
388bbb09b   Richard Purdie   [MTD] Add mtd pan...
184
185
186
187
188
189
190
191
  {
  	struct mtd_part *part = PART(mtd);
  	if (!(mtd->flags & MTD_WRITEABLE))
  		return -EROFS;
  	if (to >= mtd->size)
  		len = 0;
  	else if (to + len > mtd->size)
  		len = mtd->size - to;
b33a28873   Atsushi Nemoto   [MTD][MTDPART] Ha...
192
  	return part->master->panic_write(part->master, to + part->offset,
388bbb09b   Richard Purdie   [MTD] Add mtd pan...
193
194
  				    len, retlen, buf);
  }
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
195
  static int part_write_oob(struct mtd_info *mtd, loff_t to,
b33a28873   Atsushi Nemoto   [MTD][MTDPART] Ha...
196
  		struct mtd_oob_ops *ops)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
197
198
  {
  	struct mtd_part *part = PART(mtd);
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
199

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
200
201
  	if (!(mtd->flags & MTD_WRITEABLE))
  		return -EROFS;
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
202

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
203
  	if (to >= mtd->size)
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
204
  		return -EINVAL;
7014568ba   Vitaly Wool   [MTD] [NAND] remo...
205
  	if (ops->datbuf && to + ops->len > mtd->size)
8593fbc68   Thomas Gleixner   [MTD] Rework the ...
206
207
  		return -EINVAL;
  	return part->master->write_oob(part->master, to + part->offset, ops);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
208
  }
b33a28873   Atsushi Nemoto   [MTD][MTDPART] Ha...
209
210
  static int part_write_user_prot_reg(struct mtd_info *mtd, loff_t from,
  		size_t len, size_t *retlen, u_char *buf)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
211
212
  {
  	struct mtd_part *part = PART(mtd);
b33a28873   Atsushi Nemoto   [MTD][MTDPART] Ha...
213
  	return part->master->write_user_prot_reg(part->master, from,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
214
215
  					len, retlen, buf);
  }
b33a28873   Atsushi Nemoto   [MTD][MTDPART] Ha...
216
217
  static int part_lock_user_prot_reg(struct mtd_info *mtd, loff_t from,
  		size_t len)
f77814dd5   Nicolas Pitre   [MTD] Support for...
218
219
  {
  	struct mtd_part *part = PART(mtd);
b33a28873   Atsushi Nemoto   [MTD][MTDPART] Ha...
220
  	return part->master->lock_user_prot_reg(part->master, from, len);
f77814dd5   Nicolas Pitre   [MTD] Support for...
221
  }
b33a28873   Atsushi Nemoto   [MTD][MTDPART] Ha...
222
223
  static int part_writev(struct mtd_info *mtd, const struct kvec *vecs,
  		unsigned long count, loff_t to, size_t *retlen)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
224
225
226
227
  {
  	struct mtd_part *part = PART(mtd);
  	if (!(mtd->flags & MTD_WRITEABLE))
  		return -EROFS;
b33a28873   Atsushi Nemoto   [MTD][MTDPART] Ha...
228
  	return part->master->writev(part->master, vecs, count,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
229
  					to + part->offset, retlen);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
230
  }
b33a28873   Atsushi Nemoto   [MTD][MTDPART] Ha...
231
  static int part_erase(struct mtd_info *mtd, struct erase_info *instr)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
232
233
234
235
236
237
238
239
240
  {
  	struct mtd_part *part = PART(mtd);
  	int ret;
  	if (!(mtd->flags & MTD_WRITEABLE))
  		return -EROFS;
  	if (instr->addr >= mtd->size)
  		return -EINVAL;
  	instr->addr += part->offset;
  	ret = part->master->erase(part->master, instr);
74641d752   Adrian Hunter   [MTD] Correct par...
241
  	if (ret) {
bb0eb217c   Adrian Hunter   [MTD] Define and ...
242
  		if (instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN)
74641d752   Adrian Hunter   [MTD] Correct par...
243
244
245
  			instr->fail_addr -= part->offset;
  		instr->addr -= part->offset;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
246
247
248
249
250
251
252
  	return ret;
  }
  
  void mtd_erase_callback(struct erase_info *instr)
  {
  	if (instr->mtd->erase == part_erase) {
  		struct mtd_part *part = PART(instr->mtd);
bb0eb217c   Adrian Hunter   [MTD] Define and ...
253
  		if (instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
254
255
256
257
258
259
260
  			instr->fail_addr -= part->offset;
  		instr->addr -= part->offset;
  	}
  	if (instr->callback)
  		instr->callback(instr);
  }
  EXPORT_SYMBOL_GPL(mtd_erase_callback);
69423d99f   Adrian Hunter   [MTD] update inte...
261
  static int part_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
262
263
  {
  	struct mtd_part *part = PART(mtd);
97894cda5   Thomas Gleixner   [MTD] core: Clean...
264
  	if ((len + ofs) > mtd->size)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
265
266
267
  		return -EINVAL;
  	return part->master->lock(part->master, ofs + part->offset, len);
  }
69423d99f   Adrian Hunter   [MTD] update inte...
268
  static int part_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
269
270
  {
  	struct mtd_part *part = PART(mtd);
97894cda5   Thomas Gleixner   [MTD] core: Clean...
271
  	if ((len + ofs) > mtd->size)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
272
273
274
  		return -EINVAL;
  	return part->master->unlock(part->master, ofs + part->offset, len);
  }
9938424f0   Richard Cochran   mtd: add an ioctl...
275
276
277
278
279
280
281
  static int part_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len)
  {
  	struct mtd_part *part = PART(mtd);
  	if ((len + ofs) > mtd->size)
  		return -EINVAL;
  	return part->master->is_locked(part->master, ofs + part->offset, len);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
  static void part_sync(struct mtd_info *mtd)
  {
  	struct mtd_part *part = PART(mtd);
  	part->master->sync(part->master);
  }
  
  static int part_suspend(struct mtd_info *mtd)
  {
  	struct mtd_part *part = PART(mtd);
  	return part->master->suspend(part->master);
  }
  
  static void part_resume(struct mtd_info *mtd)
  {
  	struct mtd_part *part = PART(mtd);
  	part->master->resume(part->master);
  }
b33a28873   Atsushi Nemoto   [MTD][MTDPART] Ha...
299
  static int part_block_isbad(struct mtd_info *mtd, loff_t ofs)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
300
301
302
303
304
305
306
  {
  	struct mtd_part *part = PART(mtd);
  	if (ofs >= mtd->size)
  		return -EINVAL;
  	ofs += part->offset;
  	return part->master->block_isbad(part->master, ofs);
  }
b33a28873   Atsushi Nemoto   [MTD][MTDPART] Ha...
307
  static int part_block_markbad(struct mtd_info *mtd, loff_t ofs)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
308
309
  {
  	struct mtd_part *part = PART(mtd);
f1a28c028   Thomas Gleixner   [MTD] NAND Expose...
310
  	int res;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
311
312
313
314
315
  	if (!(mtd->flags & MTD_WRITEABLE))
  		return -EROFS;
  	if (ofs >= mtd->size)
  		return -EINVAL;
  	ofs += part->offset;
f1a28c028   Thomas Gleixner   [MTD] NAND Expose...
316
317
318
319
  	res = part->master->block_markbad(part->master, ofs);
  	if (!res)
  		mtd->ecc_stats.badblocks++;
  	return res;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
320
  }
5daa7b214   Roman Tereshonkov   mtd: prepare part...
321
322
323
324
325
  static inline void free_partition(struct mtd_part *p)
  {
  	kfree(p->mtd.name);
  	kfree(p);
  }
97894cda5   Thomas Gleixner   [MTD] core: Clean...
326
327
  /*
   * This function unregisters and destroy all slave MTD objects which are
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
328
329
330
331
332
   * attached to the given master MTD object.
   */
  
  int del_mtd_partitions(struct mtd_info *master)
  {
71a928c0e   Chris Malley   [MTD] Use list_fo...
333
  	struct mtd_part *slave, *next;
5daa7b214   Roman Tereshonkov   mtd: prepare part...
334
  	int ret, err = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
335

5daa7b214   Roman Tereshonkov   mtd: prepare part...
336
  	mutex_lock(&mtd_partitions_mutex);
71a928c0e   Chris Malley   [MTD] Use list_fo...
337
  	list_for_each_entry_safe(slave, next, &mtd_partitions, list)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
338
  		if (slave->master == master) {
5daa7b214   Roman Tereshonkov   mtd: prepare part...
339
340
341
342
343
  			ret = del_mtd_device(&slave->mtd);
  			if (ret < 0) {
  				err = ret;
  				continue;
  			}
71a928c0e   Chris Malley   [MTD] Use list_fo...
344
  			list_del(&slave->list);
5daa7b214   Roman Tereshonkov   mtd: prepare part...
345
  			free_partition(slave);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
346
  		}
5daa7b214   Roman Tereshonkov   mtd: prepare part...
347
  	mutex_unlock(&mtd_partitions_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
348

5daa7b214   Roman Tereshonkov   mtd: prepare part...
349
  	return err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
350
  }
5daa7b214   Roman Tereshonkov   mtd: prepare part...
351
352
353
  static struct mtd_part *allocate_partition(struct mtd_info *master,
  			const struct mtd_partition *part, int partno,
  			uint64_t cur_offset)
7788ba71a   Atsushi Nemoto   [MTD][MTDPART] Se...
354
355
  {
  	struct mtd_part *slave;
5daa7b214   Roman Tereshonkov   mtd: prepare part...
356
  	char *name;
7788ba71a   Atsushi Nemoto   [MTD][MTDPART] Se...
357
358
  
  	/* allocate the partition structure */
b33a28873   Atsushi Nemoto   [MTD][MTDPART] Ha...
359
  	slave = kzalloc(sizeof(*slave), GFP_KERNEL);
5daa7b214   Roman Tereshonkov   mtd: prepare part...
360
361
  	name = kstrdup(part->name, GFP_KERNEL);
  	if (!name || !slave) {
b33a28873   Atsushi Nemoto   [MTD][MTDPART] Ha...
362
363
  		printk(KERN_ERR"memory allocation error while creating partitions for \"%s\"
  ",
5daa7b214   Roman Tereshonkov   mtd: prepare part...
364
365
366
367
  		       master->name);
  		kfree(name);
  		kfree(slave);
  		return ERR_PTR(-ENOMEM);
7788ba71a   Atsushi Nemoto   [MTD][MTDPART] Se...
368
  	}
7788ba71a   Atsushi Nemoto   [MTD][MTDPART] Se...
369
370
371
372
373
374
  
  	/* 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;
7fa33ac0a   Anatolij Gustschin   mtd: initialize w...
375
  	slave->mtd.writebufsize = master->writebufsize;
7788ba71a   Atsushi Nemoto   [MTD][MTDPART] Se...
376
377
378
  	slave->mtd.oobsize = master->oobsize;
  	slave->mtd.oobavail = master->oobavail;
  	slave->mtd.subpage_sft = master->subpage_sft;
5daa7b214   Roman Tereshonkov   mtd: prepare part...
379
  	slave->mtd.name = name;
7788ba71a   Atsushi Nemoto   [MTD][MTDPART] Se...
380
  	slave->mtd.owner = master->owner;
402d32651   David Howells   NOMMU: Present ba...
381
  	slave->mtd.backing_dev_info = master->backing_dev_info;
7788ba71a   Atsushi Nemoto   [MTD][MTDPART] Se...
382

1f24b5a8e   David Brownell   [MTD] driver mode...
383
384
385
386
  	/* 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;
7788ba71a   Atsushi Nemoto   [MTD][MTDPART] Se...
387
388
389
390
391
  	slave->mtd.read = part_read;
  	slave->mtd.write = part_write;
  
  	if (master->panic_write)
  		slave->mtd.panic_write = part_panic_write;
b33a28873   Atsushi Nemoto   [MTD][MTDPART] Ha...
392
  	if (master->point && master->unpoint) {
7788ba71a   Atsushi Nemoto   [MTD][MTDPART] Se...
393
394
395
  		slave->mtd.point = part_point;
  		slave->mtd.unpoint = part_unpoint;
  	}
402d32651   David Howells   NOMMU: Present ba...
396
397
  	if (master->get_unmapped_area)
  		slave->mtd.get_unmapped_area = part_get_unmapped_area;
7788ba71a   Atsushi Nemoto   [MTD][MTDPART] Se...
398
399
400
401
  	if (master->read_oob)
  		slave->mtd.read_oob = part_read_oob;
  	if (master->write_oob)
  		slave->mtd.write_oob = part_write_oob;
b33a28873   Atsushi Nemoto   [MTD][MTDPART] Ha...
402
  	if (master->read_user_prot_reg)
7788ba71a   Atsushi Nemoto   [MTD][MTDPART] Se...
403
  		slave->mtd.read_user_prot_reg = part_read_user_prot_reg;
b33a28873   Atsushi Nemoto   [MTD][MTDPART] Ha...
404
  	if (master->read_fact_prot_reg)
7788ba71a   Atsushi Nemoto   [MTD][MTDPART] Se...
405
  		slave->mtd.read_fact_prot_reg = part_read_fact_prot_reg;
b33a28873   Atsushi Nemoto   [MTD][MTDPART] Ha...
406
  	if (master->write_user_prot_reg)
7788ba71a   Atsushi Nemoto   [MTD][MTDPART] Se...
407
  		slave->mtd.write_user_prot_reg = part_write_user_prot_reg;
b33a28873   Atsushi Nemoto   [MTD][MTDPART] Ha...
408
  	if (master->lock_user_prot_reg)
7788ba71a   Atsushi Nemoto   [MTD][MTDPART] Se...
409
  		slave->mtd.lock_user_prot_reg = part_lock_user_prot_reg;
b33a28873   Atsushi Nemoto   [MTD][MTDPART] Ha...
410
  	if (master->get_user_prot_info)
7788ba71a   Atsushi Nemoto   [MTD][MTDPART] Se...
411
  		slave->mtd.get_user_prot_info = part_get_user_prot_info;
b33a28873   Atsushi Nemoto   [MTD][MTDPART] Ha...
412
  	if (master->get_fact_prot_info)
7788ba71a   Atsushi Nemoto   [MTD][MTDPART] Se...
413
414
415
  		slave->mtd.get_fact_prot_info = part_get_fact_prot_info;
  	if (master->sync)
  		slave->mtd.sync = part_sync;
4704a7847   David Woodhouse   [MTD] Only set pa...
416
  	if (!partno && !master->dev.class && master->suspend && master->resume) {
7788ba71a   Atsushi Nemoto   [MTD][MTDPART] Se...
417
418
419
420
421
422
423
424
425
  			slave->mtd.suspend = part_suspend;
  			slave->mtd.resume = part_resume;
  	}
  	if (master->writev)
  		slave->mtd.writev = part_writev;
  	if (master->lock)
  		slave->mtd.lock = part_lock;
  	if (master->unlock)
  		slave->mtd.unlock = part_unlock;
9938424f0   Richard Cochran   mtd: add an ioctl...
426
427
  	if (master->is_locked)
  		slave->mtd.is_locked = part_is_locked;
7788ba71a   Atsushi Nemoto   [MTD][MTDPART] Se...
428
429
430
431
432
433
434
  	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;
  	slave->master = master;
  	slave->offset = part->offset;
7788ba71a   Atsushi Nemoto   [MTD][MTDPART] Se...
435
436
437
438
439
  
  	if (slave->offset == MTDPART_OFS_APPEND)
  		slave->offset = cur_offset;
  	if (slave->offset == MTDPART_OFS_NXTBLK) {
  		slave->offset = cur_offset;
69423d99f   Adrian Hunter   [MTD] update inte...
440
  		if (mtd_mod_by_eb(cur_offset, master) != 0) {
7788ba71a   Atsushi Nemoto   [MTD][MTDPART] Se...
441
  			/* Round up to next erasesize */
69423d99f   Adrian Hunter   [MTD] update inte...
442
  			slave->offset = (mtd_div_by_eb(cur_offset, master) + 1) * master->erasesize;
7788ba71a   Atsushi Nemoto   [MTD][MTDPART] Se...
443
  			printk(KERN_NOTICE "Moving partition %d: "
69423d99f   Adrian Hunter   [MTD] update inte...
444
445
446
  			       "0x%012llx -> 0x%012llx
  ", partno,
  			       (unsigned long long)cur_offset, (unsigned long long)slave->offset);
7788ba71a   Atsushi Nemoto   [MTD][MTDPART] Se...
447
448
449
450
  		}
  	}
  	if (slave->mtd.size == MTDPART_SIZ_FULL)
  		slave->mtd.size = master->size - slave->offset;
69423d99f   Adrian Hunter   [MTD] update inte...
451
452
453
  	printk(KERN_NOTICE "0x%012llx-0x%012llx : \"%s\"
  ", (unsigned long long)slave->offset,
  		(unsigned long long)(slave->offset + slave->mtd.size), slave->mtd.name);
7788ba71a   Atsushi Nemoto   [MTD][MTDPART] Se...
454
455
456
  
  	/* let's do some sanity checks */
  	if (slave->offset >= master->size) {
f636ffb42   Atsushi Nemoto   [MTD][MTDPART] Fi...
457
  		/* let's register it anyway to preserve ordering */
7788ba71a   Atsushi Nemoto   [MTD][MTDPART] Se...
458
459
  		slave->offset = 0;
  		slave->mtd.size = 0;
b33a28873   Atsushi Nemoto   [MTD][MTDPART] Ha...
460
461
  		printk(KERN_ERR"mtd: partition \"%s\" is out of reach -- disabled
  ",
7788ba71a   Atsushi Nemoto   [MTD][MTDPART] Se...
462
  			part->name);
f636ffb42   Atsushi Nemoto   [MTD][MTDPART] Fi...
463
  		goto out_register;
7788ba71a   Atsushi Nemoto   [MTD][MTDPART] Se...
464
465
466
  	}
  	if (slave->offset + slave->mtd.size > master->size) {
  		slave->mtd.size = master->size - slave->offset;
69423d99f   Adrian Hunter   [MTD] update inte...
467
468
469
  		printk(KERN_WARNING"mtd: partition \"%s\" extends beyond the end of device \"%s\" -- size truncated to %#llx
  ",
  			part->name, master->name, (unsigned long long)slave->mtd.size);
7788ba71a   Atsushi Nemoto   [MTD][MTDPART] Se...
470
  	}
b33a28873   Atsushi Nemoto   [MTD][MTDPART] Ha...
471
  	if (master->numeraseregions > 1) {
7788ba71a   Atsushi Nemoto   [MTD][MTDPART] Se...
472
  		/* Deal with variable erase size stuff */
6910c1368   Atsushi Nemoto   [MTD][MTDPART] Cl...
473
  		int i, max = master->numeraseregions;
69423d99f   Adrian Hunter   [MTD] update inte...
474
  		u64 end = slave->offset + slave->mtd.size;
7788ba71a   Atsushi Nemoto   [MTD][MTDPART] Se...
475
  		struct mtd_erase_region_info *regions = master->eraseregions;
6910c1368   Atsushi Nemoto   [MTD][MTDPART] Cl...
476
477
478
  		/* Find the first erase regions which is part of this
  		 * partition. */
  		for (i = 0; i < max && regions[i].offset <= slave->offset; i++)
7788ba71a   Atsushi Nemoto   [MTD][MTDPART] Se...
479
  			;
6910c1368   Atsushi Nemoto   [MTD][MTDPART] Cl...
480
  		/* The loop searched for the region _behind_ the first one */
a57ca0466   Roel Kluin   mtd: mtdpart: pre...
481
482
  		if (i > 0)
  			i--;
7788ba71a   Atsushi Nemoto   [MTD][MTDPART] Se...
483

6910c1368   Atsushi Nemoto   [MTD][MTDPART] Cl...
484
485
  		/* Pick biggest erasesize */
  		for (; i < max && regions[i].offset < end; i++) {
7788ba71a   Atsushi Nemoto   [MTD][MTDPART] Se...
486
487
488
489
  			if (slave->mtd.erasesize < regions[i].erasesize) {
  				slave->mtd.erasesize = regions[i].erasesize;
  			}
  		}
6910c1368   Atsushi Nemoto   [MTD][MTDPART] Cl...
490
  		BUG_ON(slave->mtd.erasesize == 0);
7788ba71a   Atsushi Nemoto   [MTD][MTDPART] Se...
491
492
493
494
495
496
  	} else {
  		/* Single erase size */
  		slave->mtd.erasesize = master->erasesize;
  	}
  
  	if ((slave->mtd.flags & MTD_WRITEABLE) &&
69423d99f   Adrian Hunter   [MTD] update inte...
497
  	    mtd_mod_by_eb(slave->offset, &slave->mtd)) {
7788ba71a   Atsushi Nemoto   [MTD][MTDPART] Se...
498
  		/* Doesn't start on a boundary of major erase size */
b33a28873   Atsushi Nemoto   [MTD][MTDPART] Ha...
499
500
  		/* FIXME: Let it be writable if it is on a boundary of
  		 * _minor_ erase size though */
7788ba71a   Atsushi Nemoto   [MTD][MTDPART] Se...
501
  		slave->mtd.flags &= ~MTD_WRITEABLE;
b33a28873   Atsushi Nemoto   [MTD][MTDPART] Ha...
502
503
  		printk(KERN_WARNING"mtd: partition \"%s\" doesn't start on an erase block boundary -- force read-only
  ",
7788ba71a   Atsushi Nemoto   [MTD][MTDPART] Se...
504
505
506
  			part->name);
  	}
  	if ((slave->mtd.flags & MTD_WRITEABLE) &&
69423d99f   Adrian Hunter   [MTD] update inte...
507
  	    mtd_mod_by_eb(slave->mtd.size, &slave->mtd)) {
7788ba71a   Atsushi Nemoto   [MTD][MTDPART] Se...
508
  		slave->mtd.flags &= ~MTD_WRITEABLE;
b33a28873   Atsushi Nemoto   [MTD][MTDPART] Ha...
509
510
  		printk(KERN_WARNING"mtd: partition \"%s\" doesn't end on an erase block -- force read-only
  ",
7788ba71a   Atsushi Nemoto   [MTD][MTDPART] Se...
511
512
513
514
515
  			part->name);
  	}
  
  	slave->mtd.ecclayout = master->ecclayout;
  	if (master->block_isbad) {
69423d99f   Adrian Hunter   [MTD] update inte...
516
  		uint64_t offs = 0;
7788ba71a   Atsushi Nemoto   [MTD][MTDPART] Se...
517

b33a28873   Atsushi Nemoto   [MTD][MTDPART] Ha...
518
  		while (offs < slave->mtd.size) {
7788ba71a   Atsushi Nemoto   [MTD][MTDPART] Se...
519
520
521
522
523
524
  			if (master->block_isbad(master,
  						offs + slave->offset))
  				slave->mtd.ecc_stats.badblocks++;
  			offs += slave->mtd.erasesize;
  		}
  	}
f636ffb42   Atsushi Nemoto   [MTD][MTDPART] Fi...
525
  out_register:
7788ba71a   Atsushi Nemoto   [MTD][MTDPART] Se...
526
527
  	return slave;
  }
5daa7b214   Roman Tereshonkov   mtd: prepare part...
528
529
530
531
532
533
534
535
536
537
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
  int mtd_add_partition(struct mtd_info *master, char *name,
  		      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);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
607
608
609
610
  /*
   * 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.
1f24b5a8e   David Brownell   [MTD] driver mode...
611
612
613
   *
   * We don't register the master, or expect the caller to have done so,
   * for reasons of data integrity.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
614
   */
97894cda5   Thomas Gleixner   [MTD] core: Clean...
615
  int add_mtd_partitions(struct mtd_info *master,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
616
617
618
619
  		       const struct mtd_partition *parts,
  		       int nbparts)
  {
  	struct mtd_part *slave;
69423d99f   Adrian Hunter   [MTD] update inte...
620
  	uint64_t cur_offset = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
621
  	int i;
b33a28873   Atsushi Nemoto   [MTD][MTDPART] Ha...
622
623
  	printk(KERN_NOTICE "Creating %d MTD partitions on \"%s\":
  ", nbparts, master->name);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
624
625
  
  	for (i = 0; i < nbparts; i++) {
5daa7b214   Roman Tereshonkov   mtd: prepare part...
626
627
628
629
630
631
632
633
634
  		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);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
635
  		cur_offset = slave->offset + slave->mtd.size;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
636
637
638
639
  	}
  
  	return 0;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
640
641
642
643
644
645
  
  static DEFINE_SPINLOCK(part_parser_lock);
  static LIST_HEAD(part_parsers);
  
  static struct mtd_part_parser *get_partition_parser(const char *name)
  {
71a928c0e   Chris Malley   [MTD] Use list_fo...
646
  	struct mtd_part_parser *p, *ret = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
647

71a928c0e   Chris Malley   [MTD] Use list_fo...
648
  	spin_lock(&part_parser_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
649

71a928c0e   Chris Malley   [MTD] Use list_fo...
650
  	list_for_each_entry(p, &part_parsers, list)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
651
652
653
654
  		if (!strcmp(p->name, name) && try_module_get(p->owner)) {
  			ret = p;
  			break;
  		}
71a928c0e   Chris Malley   [MTD] Use list_fo...
655

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
656
657
658
659
660
661
662
663
664
665
666
667
668
  	spin_unlock(&part_parser_lock);
  
  	return ret;
  }
  
  int register_mtd_parser(struct mtd_part_parser *p)
  {
  	spin_lock(&part_parser_lock);
  	list_add(&p->list, &part_parsers);
  	spin_unlock(&part_parser_lock);
  
  	return 0;
  }
b33a28873   Atsushi Nemoto   [MTD][MTDPART] Ha...
669
  EXPORT_SYMBOL_GPL(register_mtd_parser);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
670
671
672
673
674
675
676
677
  
  int deregister_mtd_parser(struct mtd_part_parser *p)
  {
  	spin_lock(&part_parser_lock);
  	list_del(&p->list);
  	spin_unlock(&part_parser_lock);
  	return 0;
  }
b33a28873   Atsushi Nemoto   [MTD][MTDPART] Ha...
678
  EXPORT_SYMBOL_GPL(deregister_mtd_parser);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
679

97894cda5   Thomas Gleixner   [MTD] core: Clean...
680
  int parse_mtd_partitions(struct mtd_info *master, const char **types,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
681
682
683
684
  			 struct mtd_partition **pparts, unsigned long origin)
  {
  	struct mtd_part_parser *parser;
  	int ret = 0;
97894cda5   Thomas Gleixner   [MTD] core: Clean...
685

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
686
687
  	for ( ; ret <= 0 && *types; types++) {
  		parser = get_partition_parser(*types);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
688
689
  		if (!parser && !request_module("%s", *types))
  				parser = get_partition_parser(*types);
7c802fbd5   Artem Bityutskiy   mtd: be silent wh...
690
  		if (!parser)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
691
  			continue;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
692
693
  		ret = (*parser->parse_fn)(master, pparts, origin);
  		if (ret > 0) {
97894cda5   Thomas Gleixner   [MTD] core: Clean...
694
695
  			printk(KERN_NOTICE "%d %s partitions found on MTD device %s
  ",
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
696
697
698
699
700
701
  			       ret, parser->name, master->name);
  		}
  		put_partition_parser(parser);
  	}
  	return ret;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
702
  EXPORT_SYMBOL_GPL(parse_mtd_partitions);
5daa7b214   Roman Tereshonkov   mtd: prepare part...
703

a7e93dcd9   Roman Tereshonkov   mtd: fix master d...
704
  int mtd_is_partition(struct mtd_info *mtd)
5daa7b214   Roman Tereshonkov   mtd: prepare part...
705
706
  {
  	struct mtd_part *part;
a7e93dcd9   Roman Tereshonkov   mtd: fix master d...
707
  	int ispart = 0;
5daa7b214   Roman Tereshonkov   mtd: prepare part...
708
709
710
711
  
  	mutex_lock(&mtd_partitions_mutex);
  	list_for_each_entry(part, &mtd_partitions, list)
  		if (&part->mtd == mtd) {
a7e93dcd9   Roman Tereshonkov   mtd: fix master d...
712
  			ispart = 1;
5daa7b214   Roman Tereshonkov   mtd: prepare part...
713
714
715
  			break;
  		}
  	mutex_unlock(&mtd_partitions_mutex);
a7e93dcd9   Roman Tereshonkov   mtd: fix master d...
716
  	return ispart;
5daa7b214   Roman Tereshonkov   mtd: prepare part...
717
  }
a7e93dcd9   Roman Tereshonkov   mtd: fix master d...
718
  EXPORT_SYMBOL_GPL(mtd_is_partition);