diff options
Diffstat (limited to 'drivers/mtd/mtdpart.c')
-rw-r--r-- | drivers/mtd/mtdpart.c | 448 |
1 files changed, 227 insertions, 221 deletions
diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index 07c701169344..edb90b58a9b1 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c | |||
@@ -5,8 +5,6 @@ | |||
5 | * | 5 | * |
6 | * This code is GPL | 6 | * This code is GPL |
7 | * | 7 | * |
8 | * $Id: mtdpart.c,v 1.55 2005/11/07 11:14:20 gleixner Exp $ | ||
9 | * | ||
10 | * 02-21-2002 Thomas Gleixner <gleixner@autronix.de> | 8 | * 02-21-2002 Thomas Gleixner <gleixner@autronix.de> |
11 | * added support for read_oob, write_oob | 9 | * added support for read_oob, write_oob |
12 | */ | 10 | */ |
@@ -46,8 +44,8 @@ struct mtd_part { | |||
46 | * to the _real_ device. | 44 | * to the _real_ device. |
47 | */ | 45 | */ |
48 | 46 | ||
49 | static int part_read (struct mtd_info *mtd, loff_t from, size_t len, | 47 | static int part_read(struct mtd_info *mtd, loff_t from, size_t len, |
50 | size_t *retlen, u_char *buf) | 48 | size_t *retlen, u_char *buf) |
51 | { | 49 | { |
52 | struct mtd_part *part = PART(mtd); | 50 | struct mtd_part *part = PART(mtd); |
53 | int res; | 51 | int res; |
@@ -56,7 +54,7 @@ static int part_read (struct mtd_info *mtd, loff_t from, size_t len, | |||
56 | len = 0; | 54 | len = 0; |
57 | else if (from + len > mtd->size) | 55 | else if (from + len > mtd->size) |
58 | len = mtd->size - from; | 56 | len = mtd->size - from; |
59 | res = part->master->read (part->master, from + part->offset, | 57 | res = part->master->read(part->master, from + part->offset, |
60 | len, retlen, buf); | 58 | len, retlen, buf); |
61 | if (unlikely(res)) { | 59 | if (unlikely(res)) { |
62 | if (res == -EUCLEAN) | 60 | if (res == -EUCLEAN) |
@@ -67,8 +65,8 @@ static int part_read (struct mtd_info *mtd, loff_t from, size_t len, | |||
67 | return res; | 65 | return res; |
68 | } | 66 | } |
69 | 67 | ||
70 | static int part_point (struct mtd_info *mtd, loff_t from, size_t len, | 68 | static int part_point(struct mtd_info *mtd, loff_t from, size_t len, |
71 | size_t *retlen, void **virt, resource_size_t *phys) | 69 | size_t *retlen, void **virt, resource_size_t *phys) |
72 | { | 70 | { |
73 | struct mtd_part *part = PART(mtd); | 71 | struct mtd_part *part = PART(mtd); |
74 | if (from >= mtd->size) | 72 | if (from >= mtd->size) |
@@ -87,7 +85,7 @@ static void part_unpoint(struct mtd_info *mtd, loff_t from, size_t len) | |||
87 | } | 85 | } |
88 | 86 | ||
89 | static int part_read_oob(struct mtd_info *mtd, loff_t from, | 87 | static int part_read_oob(struct mtd_info *mtd, loff_t from, |
90 | struct mtd_oob_ops *ops) | 88 | struct mtd_oob_ops *ops) |
91 | { | 89 | { |
92 | struct mtd_part *part = PART(mtd); | 90 | struct mtd_part *part = PART(mtd); |
93 | int res; | 91 | int res; |
@@ -107,38 +105,38 @@ static int part_read_oob(struct mtd_info *mtd, loff_t from, | |||
107 | return res; | 105 | return res; |
108 | } | 106 | } |
109 | 107 | ||
110 | static int part_read_user_prot_reg (struct mtd_info *mtd, loff_t from, size_t len, | 108 | static int part_read_user_prot_reg(struct mtd_info *mtd, loff_t from, |
111 | size_t *retlen, u_char *buf) | 109 | size_t len, size_t *retlen, u_char *buf) |
112 | { | 110 | { |
113 | struct mtd_part *part = PART(mtd); | 111 | struct mtd_part *part = PART(mtd); |
114 | return part->master->read_user_prot_reg (part->master, from, | 112 | return part->master->read_user_prot_reg(part->master, from, |
115 | len, retlen, buf); | 113 | len, retlen, buf); |
116 | } | 114 | } |
117 | 115 | ||
118 | static int part_get_user_prot_info (struct mtd_info *mtd, | 116 | static int part_get_user_prot_info(struct mtd_info *mtd, |
119 | struct otp_info *buf, size_t len) | 117 | struct otp_info *buf, size_t len) |
120 | { | 118 | { |
121 | struct mtd_part *part = PART(mtd); | 119 | struct mtd_part *part = PART(mtd); |
122 | return part->master->get_user_prot_info (part->master, buf, len); | 120 | return part->master->get_user_prot_info(part->master, buf, len); |
123 | } | 121 | } |
124 | 122 | ||
125 | static int part_read_fact_prot_reg (struct mtd_info *mtd, loff_t from, size_t len, | 123 | static int part_read_fact_prot_reg(struct mtd_info *mtd, loff_t from, |
126 | size_t *retlen, u_char *buf) | 124 | size_t len, size_t *retlen, u_char *buf) |
127 | { | 125 | { |
128 | struct mtd_part *part = PART(mtd); | 126 | struct mtd_part *part = PART(mtd); |
129 | return part->master->read_fact_prot_reg (part->master, from, | 127 | return part->master->read_fact_prot_reg(part->master, from, |
130 | len, retlen, buf); | 128 | len, retlen, buf); |
131 | } | 129 | } |
132 | 130 | ||
133 | static int part_get_fact_prot_info (struct mtd_info *mtd, | 131 | static int part_get_fact_prot_info(struct mtd_info *mtd, struct otp_info *buf, |
134 | struct otp_info *buf, size_t len) | 132 | size_t len) |
135 | { | 133 | { |
136 | struct mtd_part *part = PART(mtd); | 134 | struct mtd_part *part = PART(mtd); |
137 | return part->master->get_fact_prot_info (part->master, buf, len); | 135 | return part->master->get_fact_prot_info(part->master, buf, len); |
138 | } | 136 | } |
139 | 137 | ||
140 | static int part_write (struct mtd_info *mtd, loff_t to, size_t len, | 138 | static int part_write(struct mtd_info *mtd, loff_t to, size_t len, |
141 | size_t *retlen, const u_char *buf) | 139 | size_t *retlen, const u_char *buf) |
142 | { | 140 | { |
143 | struct mtd_part *part = PART(mtd); | 141 | struct mtd_part *part = PART(mtd); |
144 | if (!(mtd->flags & MTD_WRITEABLE)) | 142 | if (!(mtd->flags & MTD_WRITEABLE)) |
@@ -147,12 +145,12 @@ static int part_write (struct mtd_info *mtd, loff_t to, size_t len, | |||
147 | len = 0; | 145 | len = 0; |
148 | else if (to + len > mtd->size) | 146 | else if (to + len > mtd->size) |
149 | len = mtd->size - to; | 147 | len = mtd->size - to; |
150 | return part->master->write (part->master, to + part->offset, | 148 | return part->master->write(part->master, to + part->offset, |
151 | len, retlen, buf); | 149 | len, retlen, buf); |
152 | } | 150 | } |
153 | 151 | ||
154 | static int part_panic_write (struct mtd_info *mtd, loff_t to, size_t len, | 152 | static int part_panic_write(struct mtd_info *mtd, loff_t to, size_t len, |
155 | size_t *retlen, const u_char *buf) | 153 | size_t *retlen, const u_char *buf) |
156 | { | 154 | { |
157 | struct mtd_part *part = PART(mtd); | 155 | struct mtd_part *part = PART(mtd); |
158 | if (!(mtd->flags & MTD_WRITEABLE)) | 156 | if (!(mtd->flags & MTD_WRITEABLE)) |
@@ -161,12 +159,12 @@ static int part_panic_write (struct mtd_info *mtd, loff_t to, size_t len, | |||
161 | len = 0; | 159 | len = 0; |
162 | else if (to + len > mtd->size) | 160 | else if (to + len > mtd->size) |
163 | len = mtd->size - to; | 161 | len = mtd->size - to; |
164 | return part->master->panic_write (part->master, to + part->offset, | 162 | return part->master->panic_write(part->master, to + part->offset, |
165 | len, retlen, buf); | 163 | len, retlen, buf); |
166 | } | 164 | } |
167 | 165 | ||
168 | static int part_write_oob(struct mtd_info *mtd, loff_t to, | 166 | static int part_write_oob(struct mtd_info *mtd, loff_t to, |
169 | struct mtd_oob_ops *ops) | 167 | struct mtd_oob_ops *ops) |
170 | { | 168 | { |
171 | struct mtd_part *part = PART(mtd); | 169 | struct mtd_part *part = PART(mtd); |
172 | 170 | ||
@@ -180,31 +178,32 @@ static int part_write_oob(struct mtd_info *mtd, loff_t to, | |||
180 | return part->master->write_oob(part->master, to + part->offset, ops); | 178 | return part->master->write_oob(part->master, to + part->offset, ops); |
181 | } | 179 | } |
182 | 180 | ||
183 | static int part_write_user_prot_reg (struct mtd_info *mtd, loff_t from, size_t len, | 181 | static int part_write_user_prot_reg(struct mtd_info *mtd, loff_t from, |
184 | size_t *retlen, u_char *buf) | 182 | size_t len, size_t *retlen, u_char *buf) |
185 | { | 183 | { |
186 | struct mtd_part *part = PART(mtd); | 184 | struct mtd_part *part = PART(mtd); |
187 | return part->master->write_user_prot_reg (part->master, from, | 185 | return part->master->write_user_prot_reg(part->master, from, |
188 | len, retlen, buf); | 186 | len, retlen, buf); |
189 | } | 187 | } |
190 | 188 | ||
191 | static int part_lock_user_prot_reg (struct mtd_info *mtd, loff_t from, size_t len) | 189 | static int part_lock_user_prot_reg(struct mtd_info *mtd, loff_t from, |
190 | size_t len) | ||
192 | { | 191 | { |
193 | struct mtd_part *part = PART(mtd); | 192 | struct mtd_part *part = PART(mtd); |
194 | return part->master->lock_user_prot_reg (part->master, from, len); | 193 | return part->master->lock_user_prot_reg(part->master, from, len); |
195 | } | 194 | } |
196 | 195 | ||
197 | static int part_writev (struct mtd_info *mtd, const struct kvec *vecs, | 196 | static int part_writev(struct mtd_info *mtd, const struct kvec *vecs, |
198 | unsigned long count, loff_t to, size_t *retlen) | 197 | unsigned long count, loff_t to, size_t *retlen) |
199 | { | 198 | { |
200 | struct mtd_part *part = PART(mtd); | 199 | struct mtd_part *part = PART(mtd); |
201 | if (!(mtd->flags & MTD_WRITEABLE)) | 200 | if (!(mtd->flags & MTD_WRITEABLE)) |
202 | return -EROFS; | 201 | return -EROFS; |
203 | return part->master->writev (part->master, vecs, count, | 202 | return part->master->writev(part->master, vecs, count, |
204 | to + part->offset, retlen); | 203 | to + part->offset, retlen); |
205 | } | 204 | } |
206 | 205 | ||
207 | static int part_erase (struct mtd_info *mtd, struct erase_info *instr) | 206 | static int part_erase(struct mtd_info *mtd, struct erase_info *instr) |
208 | { | 207 | { |
209 | struct mtd_part *part = PART(mtd); | 208 | struct mtd_part *part = PART(mtd); |
210 | int ret; | 209 | int ret; |
@@ -236,7 +235,7 @@ void mtd_erase_callback(struct erase_info *instr) | |||
236 | } | 235 | } |
237 | EXPORT_SYMBOL_GPL(mtd_erase_callback); | 236 | EXPORT_SYMBOL_GPL(mtd_erase_callback); |
238 | 237 | ||
239 | static int part_lock (struct mtd_info *mtd, loff_t ofs, size_t len) | 238 | static int part_lock(struct mtd_info *mtd, loff_t ofs, size_t len) |
240 | { | 239 | { |
241 | struct mtd_part *part = PART(mtd); | 240 | struct mtd_part *part = PART(mtd); |
242 | if ((len + ofs) > mtd->size) | 241 | if ((len + ofs) > mtd->size) |
@@ -244,7 +243,7 @@ static int part_lock (struct mtd_info *mtd, loff_t ofs, size_t len) | |||
244 | return part->master->lock(part->master, ofs + part->offset, len); | 243 | return part->master->lock(part->master, ofs + part->offset, len); |
245 | } | 244 | } |
246 | 245 | ||
247 | static int part_unlock (struct mtd_info *mtd, loff_t ofs, size_t len) | 246 | static int part_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) |
248 | { | 247 | { |
249 | struct mtd_part *part = PART(mtd); | 248 | struct mtd_part *part = PART(mtd); |
250 | if ((len + ofs) > mtd->size) | 249 | if ((len + ofs) > mtd->size) |
@@ -270,7 +269,7 @@ static void part_resume(struct mtd_info *mtd) | |||
270 | part->master->resume(part->master); | 269 | part->master->resume(part->master); |
271 | } | 270 | } |
272 | 271 | ||
273 | static int part_block_isbad (struct mtd_info *mtd, loff_t ofs) | 272 | static int part_block_isbad(struct mtd_info *mtd, loff_t ofs) |
274 | { | 273 | { |
275 | struct mtd_part *part = PART(mtd); | 274 | struct mtd_part *part = PART(mtd); |
276 | if (ofs >= mtd->size) | 275 | if (ofs >= mtd->size) |
@@ -279,7 +278,7 @@ static int part_block_isbad (struct mtd_info *mtd, loff_t ofs) | |||
279 | return part->master->block_isbad(part->master, ofs); | 278 | return part->master->block_isbad(part->master, ofs); |
280 | } | 279 | } |
281 | 280 | ||
282 | static int part_block_markbad (struct mtd_info *mtd, loff_t ofs) | 281 | static int part_block_markbad(struct mtd_info *mtd, loff_t ofs) |
283 | { | 282 | { |
284 | struct mtd_part *part = PART(mtd); | 283 | struct mtd_part *part = PART(mtd); |
285 | int res; | 284 | int res; |
@@ -302,229 +301,237 @@ static int part_block_markbad (struct mtd_info *mtd, loff_t ofs) | |||
302 | 301 | ||
303 | int del_mtd_partitions(struct mtd_info *master) | 302 | int del_mtd_partitions(struct mtd_info *master) |
304 | { | 303 | { |
305 | struct list_head *node; | 304 | struct mtd_part *slave, *next; |
306 | struct mtd_part *slave; | ||
307 | 305 | ||
308 | for (node = mtd_partitions.next; | 306 | list_for_each_entry_safe(slave, next, &mtd_partitions, list) |
309 | node != &mtd_partitions; | ||
310 | node = node->next) { | ||
311 | slave = list_entry(node, struct mtd_part, list); | ||
312 | if (slave->master == master) { | 307 | if (slave->master == master) { |
313 | struct list_head *prev = node->prev; | 308 | list_del(&slave->list); |
314 | __list_del(prev, node->next); | 309 | if (slave->registered) |
315 | if(slave->registered) | ||
316 | del_mtd_device(&slave->mtd); | 310 | del_mtd_device(&slave->mtd); |
317 | kfree(slave); | 311 | kfree(slave); |
318 | node = prev; | ||
319 | } | 312 | } |
320 | } | ||
321 | 313 | ||
322 | return 0; | 314 | return 0; |
323 | } | 315 | } |
316 | EXPORT_SYMBOL(del_mtd_partitions); | ||
324 | 317 | ||
325 | /* | 318 | static struct mtd_part *add_one_partition(struct mtd_info *master, |
326 | * This function, given a master MTD object and a partition table, creates | 319 | const struct mtd_partition *part, int partno, |
327 | * and registers slave MTD objects which are bound to the master according to | 320 | u_int32_t cur_offset) |
328 | * the partition definitions. | ||
329 | * (Q: should we register the master MTD object as well?) | ||
330 | */ | ||
331 | |||
332 | int add_mtd_partitions(struct mtd_info *master, | ||
333 | const struct mtd_partition *parts, | ||
334 | int nbparts) | ||
335 | { | 321 | { |
336 | struct mtd_part *slave; | 322 | struct mtd_part *slave; |
337 | u_int32_t cur_offset = 0; | ||
338 | int i; | ||
339 | |||
340 | printk (KERN_NOTICE "Creating %d MTD partitions on \"%s\":\n", nbparts, master->name); | ||
341 | |||
342 | for (i = 0; i < nbparts; i++) { | ||
343 | 323 | ||
344 | /* allocate the partition structure */ | 324 | /* allocate the partition structure */ |
345 | slave = kzalloc (sizeof(*slave), GFP_KERNEL); | 325 | slave = kzalloc(sizeof(*slave), GFP_KERNEL); |
346 | if (!slave) { | 326 | if (!slave) { |
347 | printk ("memory allocation error while creating partitions for \"%s\"\n", | 327 | printk(KERN_ERR"memory allocation error while creating partitions for \"%s\"\n", |
348 | master->name); | 328 | master->name); |
349 | del_mtd_partitions(master); | 329 | del_mtd_partitions(master); |
350 | return -ENOMEM; | 330 | return NULL; |
351 | } | 331 | } |
352 | list_add(&slave->list, &mtd_partitions); | 332 | list_add(&slave->list, &mtd_partitions); |
353 | 333 | ||
354 | /* set up the MTD object for this partition */ | 334 | /* set up the MTD object for this partition */ |
355 | slave->mtd.type = master->type; | 335 | slave->mtd.type = master->type; |
356 | slave->mtd.flags = master->flags & ~parts[i].mask_flags; | 336 | slave->mtd.flags = master->flags & ~part->mask_flags; |
357 | slave->mtd.size = parts[i].size; | 337 | slave->mtd.size = part->size; |
358 | slave->mtd.writesize = master->writesize; | 338 | slave->mtd.writesize = master->writesize; |
359 | slave->mtd.oobsize = master->oobsize; | 339 | slave->mtd.oobsize = master->oobsize; |
360 | slave->mtd.oobavail = master->oobavail; | 340 | slave->mtd.oobavail = master->oobavail; |
361 | slave->mtd.subpage_sft = master->subpage_sft; | 341 | slave->mtd.subpage_sft = master->subpage_sft; |
362 | 342 | ||
363 | slave->mtd.name = parts[i].name; | 343 | slave->mtd.name = part->name; |
364 | slave->mtd.owner = master->owner; | 344 | slave->mtd.owner = master->owner; |
365 | 345 | ||
366 | slave->mtd.read = part_read; | 346 | slave->mtd.read = part_read; |
367 | slave->mtd.write = part_write; | 347 | slave->mtd.write = part_write; |
368 | 348 | ||
369 | if (master->panic_write) | 349 | if (master->panic_write) |
370 | slave->mtd.panic_write = part_panic_write; | 350 | slave->mtd.panic_write = part_panic_write; |
371 | 351 | ||
372 | if(master->point && master->unpoint){ | 352 | if (master->point && master->unpoint) { |
373 | slave->mtd.point = part_point; | 353 | slave->mtd.point = part_point; |
374 | slave->mtd.unpoint = part_unpoint; | 354 | slave->mtd.unpoint = part_unpoint; |
375 | } | 355 | } |
376 | 356 | ||
377 | if (master->read_oob) | 357 | if (master->read_oob) |
378 | slave->mtd.read_oob = part_read_oob; | 358 | slave->mtd.read_oob = part_read_oob; |
379 | if (master->write_oob) | 359 | if (master->write_oob) |
380 | slave->mtd.write_oob = part_write_oob; | 360 | slave->mtd.write_oob = part_write_oob; |
381 | if(master->read_user_prot_reg) | 361 | if (master->read_user_prot_reg) |
382 | slave->mtd.read_user_prot_reg = part_read_user_prot_reg; | 362 | slave->mtd.read_user_prot_reg = part_read_user_prot_reg; |
383 | if(master->read_fact_prot_reg) | 363 | if (master->read_fact_prot_reg) |
384 | slave->mtd.read_fact_prot_reg = part_read_fact_prot_reg; | 364 | slave->mtd.read_fact_prot_reg = part_read_fact_prot_reg; |
385 | if(master->write_user_prot_reg) | 365 | if (master->write_user_prot_reg) |
386 | slave->mtd.write_user_prot_reg = part_write_user_prot_reg; | 366 | slave->mtd.write_user_prot_reg = part_write_user_prot_reg; |
387 | if(master->lock_user_prot_reg) | 367 | if (master->lock_user_prot_reg) |
388 | slave->mtd.lock_user_prot_reg = part_lock_user_prot_reg; | 368 | slave->mtd.lock_user_prot_reg = part_lock_user_prot_reg; |
389 | if(master->get_user_prot_info) | 369 | if (master->get_user_prot_info) |
390 | slave->mtd.get_user_prot_info = part_get_user_prot_info; | 370 | slave->mtd.get_user_prot_info = part_get_user_prot_info; |
391 | if(master->get_fact_prot_info) | 371 | if (master->get_fact_prot_info) |
392 | slave->mtd.get_fact_prot_info = part_get_fact_prot_info; | 372 | slave->mtd.get_fact_prot_info = part_get_fact_prot_info; |
393 | if (master->sync) | 373 | if (master->sync) |
394 | slave->mtd.sync = part_sync; | 374 | slave->mtd.sync = part_sync; |
395 | if (!i && master->suspend && master->resume) { | 375 | if (!partno && master->suspend && master->resume) { |
396 | slave->mtd.suspend = part_suspend; | 376 | slave->mtd.suspend = part_suspend; |
397 | slave->mtd.resume = part_resume; | 377 | slave->mtd.resume = part_resume; |
378 | } | ||
379 | if (master->writev) | ||
380 | slave->mtd.writev = part_writev; | ||
381 | if (master->lock) | ||
382 | slave->mtd.lock = part_lock; | ||
383 | if (master->unlock) | ||
384 | slave->mtd.unlock = part_unlock; | ||
385 | if (master->block_isbad) | ||
386 | slave->mtd.block_isbad = part_block_isbad; | ||
387 | if (master->block_markbad) | ||
388 | slave->mtd.block_markbad = part_block_markbad; | ||
389 | slave->mtd.erase = part_erase; | ||
390 | slave->master = master; | ||
391 | slave->offset = part->offset; | ||
392 | slave->index = partno; | ||
393 | |||
394 | if (slave->offset == MTDPART_OFS_APPEND) | ||
395 | slave->offset = cur_offset; | ||
396 | if (slave->offset == MTDPART_OFS_NXTBLK) { | ||
397 | slave->offset = cur_offset; | ||
398 | if ((cur_offset % master->erasesize) != 0) { | ||
399 | /* Round up to next erasesize */ | ||
400 | slave->offset = ((cur_offset / master->erasesize) + 1) * master->erasesize; | ||
401 | printk(KERN_NOTICE "Moving partition %d: " | ||
402 | "0x%08x -> 0x%08x\n", partno, | ||
403 | cur_offset, slave->offset); | ||
398 | } | 404 | } |
399 | if (master->writev) | 405 | } |
400 | slave->mtd.writev = part_writev; | 406 | if (slave->mtd.size == MTDPART_SIZ_FULL) |
401 | if (master->lock) | 407 | slave->mtd.size = master->size - slave->offset; |
402 | slave->mtd.lock = part_lock; | 408 | |
403 | if (master->unlock) | 409 | printk(KERN_NOTICE "0x%08x-0x%08x : \"%s\"\n", slave->offset, |
404 | slave->mtd.unlock = part_unlock; | 410 | slave->offset + slave->mtd.size, slave->mtd.name); |
405 | if (master->block_isbad) | 411 | |
406 | slave->mtd.block_isbad = part_block_isbad; | 412 | /* let's do some sanity checks */ |
407 | if (master->block_markbad) | 413 | if (slave->offset >= master->size) { |
408 | slave->mtd.block_markbad = part_block_markbad; | 414 | /* let's register it anyway to preserve ordering */ |
409 | slave->mtd.erase = part_erase; | 415 | slave->offset = 0; |
410 | slave->master = master; | 416 | slave->mtd.size = 0; |
411 | slave->offset = parts[i].offset; | 417 | printk(KERN_ERR"mtd: partition \"%s\" is out of reach -- disabled\n", |
412 | slave->index = i; | 418 | part->name); |
413 | 419 | goto out_register; | |
414 | if (slave->offset == MTDPART_OFS_APPEND) | 420 | } |
415 | slave->offset = cur_offset; | 421 | if (slave->offset + slave->mtd.size > master->size) { |
416 | if (slave->offset == MTDPART_OFS_NXTBLK) { | 422 | slave->mtd.size = master->size - slave->offset; |
417 | slave->offset = cur_offset; | 423 | printk(KERN_WARNING"mtd: partition \"%s\" extends beyond the end of device \"%s\" -- size truncated to %#x\n", |
418 | if ((cur_offset % master->erasesize) != 0) { | 424 | part->name, master->name, slave->mtd.size); |
419 | /* Round up to next erasesize */ | 425 | } |
420 | slave->offset = ((cur_offset / master->erasesize) + 1) * master->erasesize; | 426 | if (master->numeraseregions > 1) { |
421 | printk(KERN_NOTICE "Moving partition %d: " | 427 | /* Deal with variable erase size stuff */ |
422 | "0x%08x -> 0x%08x\n", i, | 428 | int i, max = master->numeraseregions; |
423 | cur_offset, slave->offset); | 429 | u32 end = slave->offset + slave->mtd.size; |
430 | struct mtd_erase_region_info *regions = master->eraseregions; | ||
431 | |||
432 | /* Find the first erase regions which is part of this | ||
433 | * partition. */ | ||
434 | for (i = 0; i < max && regions[i].offset <= slave->offset; i++) | ||
435 | ; | ||
436 | /* The loop searched for the region _behind_ the first one */ | ||
437 | i--; | ||
438 | |||
439 | /* Pick biggest erasesize */ | ||
440 | for (; i < max && regions[i].offset < end; i++) { | ||
441 | if (slave->mtd.erasesize < regions[i].erasesize) { | ||
442 | slave->mtd.erasesize = regions[i].erasesize; | ||
424 | } | 443 | } |
425 | } | 444 | } |
426 | if (slave->mtd.size == MTDPART_SIZ_FULL) | 445 | BUG_ON(slave->mtd.erasesize == 0); |
427 | slave->mtd.size = master->size - slave->offset; | 446 | } else { |
428 | cur_offset = slave->offset + slave->mtd.size; | 447 | /* Single erase size */ |
448 | slave->mtd.erasesize = master->erasesize; | ||
449 | } | ||
429 | 450 | ||
430 | printk (KERN_NOTICE "0x%08x-0x%08x : \"%s\"\n", slave->offset, | 451 | if ((slave->mtd.flags & MTD_WRITEABLE) && |
431 | slave->offset + slave->mtd.size, slave->mtd.name); | 452 | (slave->offset % slave->mtd.erasesize)) { |
453 | /* Doesn't start on a boundary of major erase size */ | ||
454 | /* FIXME: Let it be writable if it is on a boundary of | ||
455 | * _minor_ erase size though */ | ||
456 | slave->mtd.flags &= ~MTD_WRITEABLE; | ||
457 | printk(KERN_WARNING"mtd: partition \"%s\" doesn't start on an erase block boundary -- force read-only\n", | ||
458 | part->name); | ||
459 | } | ||
460 | if ((slave->mtd.flags & MTD_WRITEABLE) && | ||
461 | (slave->mtd.size % slave->mtd.erasesize)) { | ||
462 | slave->mtd.flags &= ~MTD_WRITEABLE; | ||
463 | printk(KERN_WARNING"mtd: partition \"%s\" doesn't end on an erase block -- force read-only\n", | ||
464 | part->name); | ||
465 | } | ||
432 | 466 | ||
433 | /* let's do some sanity checks */ | 467 | slave->mtd.ecclayout = master->ecclayout; |
434 | if (slave->offset >= master->size) { | 468 | if (master->block_isbad) { |
435 | /* let's register it anyway to preserve ordering */ | 469 | uint32_t offs = 0; |
436 | slave->offset = 0; | ||
437 | slave->mtd.size = 0; | ||
438 | printk ("mtd: partition \"%s\" is out of reach -- disabled\n", | ||
439 | parts[i].name); | ||
440 | } | ||
441 | if (slave->offset + slave->mtd.size > master->size) { | ||
442 | slave->mtd.size = master->size - slave->offset; | ||
443 | printk ("mtd: partition \"%s\" extends beyond the end of device \"%s\" -- size truncated to %#x\n", | ||
444 | parts[i].name, master->name, slave->mtd.size); | ||
445 | } | ||
446 | if (master->numeraseregions>1) { | ||
447 | /* Deal with variable erase size stuff */ | ||
448 | int i; | ||
449 | struct mtd_erase_region_info *regions = master->eraseregions; | ||
450 | |||
451 | /* Find the first erase regions which is part of this partition. */ | ||
452 | for (i=0; i < master->numeraseregions && slave->offset >= regions[i].offset; i++) | ||
453 | ; | ||
454 | |||
455 | for (i--; i < master->numeraseregions && slave->offset + slave->mtd.size > regions[i].offset; i++) { | ||
456 | if (slave->mtd.erasesize < regions[i].erasesize) { | ||
457 | slave->mtd.erasesize = regions[i].erasesize; | ||
458 | } | ||
459 | } | ||
460 | } else { | ||
461 | /* Single erase size */ | ||
462 | slave->mtd.erasesize = master->erasesize; | ||
463 | } | ||
464 | 470 | ||
465 | if ((slave->mtd.flags & MTD_WRITEABLE) && | 471 | while (offs < slave->mtd.size) { |
466 | (slave->offset % slave->mtd.erasesize)) { | 472 | if (master->block_isbad(master, |
467 | /* Doesn't start on a boundary of major erase size */ | 473 | offs + slave->offset)) |
468 | /* FIXME: Let it be writable if it is on a boundary of _minor_ erase size though */ | 474 | slave->mtd.ecc_stats.badblocks++; |
469 | slave->mtd.flags &= ~MTD_WRITEABLE; | 475 | offs += slave->mtd.erasesize; |
470 | printk ("mtd: partition \"%s\" doesn't start on an erase block boundary -- force read-only\n", | ||
471 | parts[i].name); | ||
472 | } | ||
473 | if ((slave->mtd.flags & MTD_WRITEABLE) && | ||
474 | (slave->mtd.size % slave->mtd.erasesize)) { | ||
475 | slave->mtd.flags &= ~MTD_WRITEABLE; | ||
476 | printk ("mtd: partition \"%s\" doesn't end on an erase block -- force read-only\n", | ||
477 | parts[i].name); | ||
478 | } | 476 | } |
477 | } | ||
479 | 478 | ||
480 | slave->mtd.ecclayout = master->ecclayout; | 479 | out_register: |
481 | if (master->block_isbad) { | 480 | if (part->mtdp) { |
482 | uint32_t offs = 0; | 481 | /* store the object pointer (caller may or may not register it*/ |
482 | *part->mtdp = &slave->mtd; | ||
483 | slave->registered = 0; | ||
484 | } else { | ||
485 | /* register our partition */ | ||
486 | add_mtd_device(&slave->mtd); | ||
487 | slave->registered = 1; | ||
488 | } | ||
489 | return slave; | ||
490 | } | ||
483 | 491 | ||
484 | while(offs < slave->mtd.size) { | 492 | /* |
485 | if (master->block_isbad(master, | 493 | * This function, given a master MTD object and a partition table, creates |
486 | offs + slave->offset)) | 494 | * and registers slave MTD objects which are bound to the master according to |
487 | slave->mtd.ecc_stats.badblocks++; | 495 | * the partition definitions. |
488 | offs += slave->mtd.erasesize; | 496 | * (Q: should we register the master MTD object as well?) |
489 | } | 497 | */ |
490 | } | ||
491 | 498 | ||
492 | if(parts[i].mtdp) | 499 | int add_mtd_partitions(struct mtd_info *master, |
493 | { /* store the object pointer (caller may or may not register it */ | 500 | const struct mtd_partition *parts, |
494 | *parts[i].mtdp = &slave->mtd; | 501 | int nbparts) |
495 | slave->registered = 0; | 502 | { |
496 | } | 503 | struct mtd_part *slave; |
497 | else | 504 | u_int32_t cur_offset = 0; |
498 | { | 505 | int i; |
499 | /* register our partition */ | 506 | |
500 | add_mtd_device(&slave->mtd); | 507 | printk(KERN_NOTICE "Creating %d MTD partitions on \"%s\":\n", nbparts, master->name); |
501 | slave->registered = 1; | 508 | |
502 | } | 509 | for (i = 0; i < nbparts; i++) { |
510 | slave = add_one_partition(master, parts + i, i, cur_offset); | ||
511 | if (!slave) | ||
512 | return -ENOMEM; | ||
513 | cur_offset = slave->offset + slave->mtd.size; | ||
503 | } | 514 | } |
504 | 515 | ||
505 | return 0; | 516 | return 0; |
506 | } | 517 | } |
507 | |||
508 | EXPORT_SYMBOL(add_mtd_partitions); | 518 | EXPORT_SYMBOL(add_mtd_partitions); |
509 | EXPORT_SYMBOL(del_mtd_partitions); | ||
510 | 519 | ||
511 | static DEFINE_SPINLOCK(part_parser_lock); | 520 | static DEFINE_SPINLOCK(part_parser_lock); |
512 | static LIST_HEAD(part_parsers); | 521 | static LIST_HEAD(part_parsers); |
513 | 522 | ||
514 | static struct mtd_part_parser *get_partition_parser(const char *name) | 523 | static struct mtd_part_parser *get_partition_parser(const char *name) |
515 | { | 524 | { |
516 | struct list_head *this; | 525 | struct mtd_part_parser *p, *ret = NULL; |
517 | void *ret = NULL; | ||
518 | spin_lock(&part_parser_lock); | ||
519 | 526 | ||
520 | list_for_each(this, &part_parsers) { | 527 | spin_lock(&part_parser_lock); |
521 | struct mtd_part_parser *p = list_entry(this, struct mtd_part_parser, list); | ||
522 | 528 | ||
529 | list_for_each_entry(p, &part_parsers, list) | ||
523 | if (!strcmp(p->name, name) && try_module_get(p->owner)) { | 530 | if (!strcmp(p->name, name) && try_module_get(p->owner)) { |
524 | ret = p; | 531 | ret = p; |
525 | break; | 532 | break; |
526 | } | 533 | } |
527 | } | 534 | |
528 | spin_unlock(&part_parser_lock); | 535 | spin_unlock(&part_parser_lock); |
529 | 536 | ||
530 | return ret; | 537 | return ret; |
@@ -538,6 +545,7 @@ int register_mtd_parser(struct mtd_part_parser *p) | |||
538 | 545 | ||
539 | return 0; | 546 | return 0; |
540 | } | 547 | } |
548 | EXPORT_SYMBOL_GPL(register_mtd_parser); | ||
541 | 549 | ||
542 | int deregister_mtd_parser(struct mtd_part_parser *p) | 550 | int deregister_mtd_parser(struct mtd_part_parser *p) |
543 | { | 551 | { |
@@ -546,6 +554,7 @@ int deregister_mtd_parser(struct mtd_part_parser *p) | |||
546 | spin_unlock(&part_parser_lock); | 554 | spin_unlock(&part_parser_lock); |
547 | return 0; | 555 | return 0; |
548 | } | 556 | } |
557 | EXPORT_SYMBOL_GPL(deregister_mtd_parser); | ||
549 | 558 | ||
550 | int parse_mtd_partitions(struct mtd_info *master, const char **types, | 559 | int parse_mtd_partitions(struct mtd_info *master, const char **types, |
551 | struct mtd_partition **pparts, unsigned long origin) | 560 | struct mtd_partition **pparts, unsigned long origin) |
@@ -573,7 +582,4 @@ int parse_mtd_partitions(struct mtd_info *master, const char **types, | |||
573 | } | 582 | } |
574 | return ret; | 583 | return ret; |
575 | } | 584 | } |
576 | |||
577 | EXPORT_SYMBOL_GPL(parse_mtd_partitions); | 585 | EXPORT_SYMBOL_GPL(parse_mtd_partitions); |
578 | EXPORT_SYMBOL_GPL(register_mtd_parser); | ||
579 | EXPORT_SYMBOL_GPL(deregister_mtd_parser); | ||