Blame view

drivers/mtd/mtdcore.c 49.1 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
  /*
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
2
3
4
   * Core registration and callback routines for MTD
   * drivers and users.
   *
a1452a377   David Woodhouse   mtd: Update copyr...
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
   * Copyright © 1999-2010 David Woodhouse <dwmw2@infradead.org>
   * Copyright © 2006      Red Hat UK Limited 
   *
   * 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
22
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
23
24
  #include <linux/module.h>
  #include <linux/kernel.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
25
  #include <linux/ptrace.h>
447d9bd82   Alexey Dobriyan   mtd: convert to s...
26
  #include <linux/seq_file.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
27
28
29
30
  #include <linux/string.h>
  #include <linux/timer.h>
  #include <linux/major.h>
  #include <linux/fs.h>
7799308f3   Artem Bityutskiy   [MTD] add get_mtd...
31
  #include <linux/err.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
32
33
  #include <linux/ioctl.h>
  #include <linux/init.h>
215a02fd3   Brian Norris   mtd: grab a refer...
34
  #include <linux/of.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
35
  #include <linux/proc_fs.h>
b520e412f   Ben Hutchings   mtd: Replace stat...
36
  #include <linux/idr.h>
a33eb6b91   Jörn Engel   Move mtd_bdi_*map...
37
  #include <linux/backing-dev.h>
05d71b462   Tejun Heo   mtd: update gfp/s...
38
  #include <linux/gfp.h>
0d01ff258   David Howells   Include missing l...
39
  #include <linux/slab.h>
3efe41be2   Brian Norris   mtd: implement co...
40
  #include <linux/reboot.h>
fea728c09   Ezequiel Garcia   mtd: Hook I/O act...
41
  #include <linux/leds.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
42
43
  
  #include <linux/mtd/mtd.h>
f5671ab3f   Jamie Iles   mtd: introduce mt...
44
  #include <linux/mtd/partitions.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
45

356d70f19   Ben Dooks   [MTD] mtdcore.c: ...
46
  #include "mtdcore.h"
660685d9d   Artem Bityutskiy   mtd: merge mtdcha...
47

fa06052d6   Jan Kara   mtd: Convert to d...
48
  struct backing_dev_info *mtd_bdi;
356d70f19   Ben Dooks   [MTD] mtdcore.c: ...
49

57b8045d1   Lars-Peter Clausen   mtd: Switch to PM...
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
  #ifdef CONFIG_PM_SLEEP
  
  static int mtd_cls_suspend(struct device *dev)
  {
  	struct mtd_info *mtd = dev_get_drvdata(dev);
  
  	return mtd ? mtd_suspend(mtd) : 0;
  }
  
  static int mtd_cls_resume(struct device *dev)
  {
  	struct mtd_info *mtd = dev_get_drvdata(dev);
  
  	if (mtd)
  		mtd_resume(mtd);
  	return 0;
  }
  
  static SIMPLE_DEV_PM_OPS(mtd_cls_pm_ops, mtd_cls_suspend, mtd_cls_resume);
  #define MTD_CLS_PM_OPS (&mtd_cls_pm_ops)
  #else
  #define MTD_CLS_PM_OPS NULL
  #endif
15bce40cb   David Woodhouse   [MTD] Restore sus...
73
74
75
76
  
  static struct class mtd_class = {
  	.name = "mtd",
  	.owner = THIS_MODULE,
57b8045d1   Lars-Peter Clausen   mtd: Switch to PM...
77
  	.pm = MTD_CLS_PM_OPS,
15bce40cb   David Woodhouse   [MTD] Restore sus...
78
  };
1f24b5a8e   David Brownell   [MTD] driver mode...
79

b520e412f   Ben Hutchings   mtd: Replace stat...
80
  static DEFINE_IDR(mtd_idr);
97894cda5   Thomas Gleixner   [MTD] core: Clean...
81
  /* These are exported solely for the purpose of mtd_blkdevs.c. You
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
82
     should not use them for _anything_ else */
48b192686   Ingo Molnar   [PATCH] sem2mutex...
83
  DEFINE_MUTEX(mtd_table_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
84
  EXPORT_SYMBOL_GPL(mtd_table_mutex);
b520e412f   Ben Hutchings   mtd: Replace stat...
85
86
87
88
89
90
  
  struct mtd_info *__mtd_next_device(int i)
  {
  	return idr_get_next(&mtd_idr, &i);
  }
  EXPORT_SYMBOL_GPL(__mtd_next_device);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
91
92
  
  static LIST_HEAD(mtd_notifiers);
1f24b5a8e   David Brownell   [MTD] driver mode...
93

1f24b5a8e   David Brownell   [MTD] driver mode...
94
  #define MTD_DEVT(index) MKDEV(MTD_CHAR_MAJOR, (index)*2)
1f24b5a8e   David Brownell   [MTD] driver mode...
95
96
97
98
99
100
  
  /* REVISIT once MTD uses the driver model better, whoever allocates
   * the mtd_info will probably want to use the release() hook...
   */
  static void mtd_release(struct device *dev)
  {
5e4721283   Brian Norris   mtd: remove dead ...
101
  	struct mtd_info *mtd = dev_get_drvdata(dev);
d5de20a9a   Artem Bityutskiy   mtd: kill dev_to_...
102
  	dev_t index = MTD_DEVT(mtd->index);
1f24b5a8e   David Brownell   [MTD] driver mode...
103

5e4721283   Brian Norris   mtd: remove dead ...
104
105
  	/* remove /dev/mtdXro node */
  	device_destroy(&mtd_class, index + 1);
15bce40cb   David Woodhouse   [MTD] Restore sus...
106
  }
1f24b5a8e   David Brownell   [MTD] driver mode...
107
108
109
  static ssize_t mtd_type_show(struct device *dev,
  		struct device_attribute *attr, char *buf)
  {
d5de20a9a   Artem Bityutskiy   mtd: kill dev_to_...
110
  	struct mtd_info *mtd = dev_get_drvdata(dev);
1f24b5a8e   David Brownell   [MTD] driver mode...
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
  	char *type;
  
  	switch (mtd->type) {
  	case MTD_ABSENT:
  		type = "absent";
  		break;
  	case MTD_RAM:
  		type = "ram";
  		break;
  	case MTD_ROM:
  		type = "rom";
  		break;
  	case MTD_NORFLASH:
  		type = "nor";
  		break;
  	case MTD_NANDFLASH:
  		type = "nand";
  		break;
  	case MTD_DATAFLASH:
  		type = "dataflash";
  		break;
  	case MTD_UBIVOLUME:
  		type = "ubi";
  		break;
f48372465   Huang Shijie   mtd: add MTD_MLCN...
135
136
137
  	case MTD_MLCNANDFLASH:
  		type = "mlc-nand";
  		break;
1f24b5a8e   David Brownell   [MTD] driver mode...
138
139
140
141
142
143
144
  	default:
  		type = "unknown";
  	}
  
  	return snprintf(buf, PAGE_SIZE, "%s
  ", type);
  }
694bb7fc1   Kevin Cernekee   [MTD] driver mode...
145
146
147
148
149
  static DEVICE_ATTR(type, S_IRUGO, mtd_type_show, NULL);
  
  static ssize_t mtd_flags_show(struct device *dev,
  		struct device_attribute *attr, char *buf)
  {
d5de20a9a   Artem Bityutskiy   mtd: kill dev_to_...
150
  	struct mtd_info *mtd = dev_get_drvdata(dev);
694bb7fc1   Kevin Cernekee   [MTD] driver mode...
151
152
153
154
155
156
157
158
159
160
  
  	return snprintf(buf, PAGE_SIZE, "0x%lx
  ", (unsigned long)mtd->flags);
  
  }
  static DEVICE_ATTR(flags, S_IRUGO, mtd_flags_show, NULL);
  
  static ssize_t mtd_size_show(struct device *dev,
  		struct device_attribute *attr, char *buf)
  {
d5de20a9a   Artem Bityutskiy   mtd: kill dev_to_...
161
  	struct mtd_info *mtd = dev_get_drvdata(dev);
694bb7fc1   Kevin Cernekee   [MTD] driver mode...
162
163
164
165
166
167
168
169
170
171
172
  
  	return snprintf(buf, PAGE_SIZE, "%llu
  ",
  		(unsigned long long)mtd->size);
  
  }
  static DEVICE_ATTR(size, S_IRUGO, mtd_size_show, NULL);
  
  static ssize_t mtd_erasesize_show(struct device *dev,
  		struct device_attribute *attr, char *buf)
  {
d5de20a9a   Artem Bityutskiy   mtd: kill dev_to_...
173
  	struct mtd_info *mtd = dev_get_drvdata(dev);
694bb7fc1   Kevin Cernekee   [MTD] driver mode...
174
175
176
177
178
179
180
181
182
183
  
  	return snprintf(buf, PAGE_SIZE, "%lu
  ", (unsigned long)mtd->erasesize);
  
  }
  static DEVICE_ATTR(erasesize, S_IRUGO, mtd_erasesize_show, NULL);
  
  static ssize_t mtd_writesize_show(struct device *dev,
  		struct device_attribute *attr, char *buf)
  {
d5de20a9a   Artem Bityutskiy   mtd: kill dev_to_...
184
  	struct mtd_info *mtd = dev_get_drvdata(dev);
694bb7fc1   Kevin Cernekee   [MTD] driver mode...
185
186
187
188
189
190
  
  	return snprintf(buf, PAGE_SIZE, "%lu
  ", (unsigned long)mtd->writesize);
  
  }
  static DEVICE_ATTR(writesize, S_IRUGO, mtd_writesize_show, NULL);
e76935489   Artem Bityutskiy   mtd: expose subpa...
191
192
193
  static ssize_t mtd_subpagesize_show(struct device *dev,
  		struct device_attribute *attr, char *buf)
  {
d5de20a9a   Artem Bityutskiy   mtd: kill dev_to_...
194
  	struct mtd_info *mtd = dev_get_drvdata(dev);
e76935489   Artem Bityutskiy   mtd: expose subpa...
195
196
197
198
199
200
201
  	unsigned int subpagesize = mtd->writesize >> mtd->subpage_sft;
  
  	return snprintf(buf, PAGE_SIZE, "%u
  ", subpagesize);
  
  }
  static DEVICE_ATTR(subpagesize, S_IRUGO, mtd_subpagesize_show, NULL);
694bb7fc1   Kevin Cernekee   [MTD] driver mode...
202
203
204
  static ssize_t mtd_oobsize_show(struct device *dev,
  		struct device_attribute *attr, char *buf)
  {
d5de20a9a   Artem Bityutskiy   mtd: kill dev_to_...
205
  	struct mtd_info *mtd = dev_get_drvdata(dev);
694bb7fc1   Kevin Cernekee   [MTD] driver mode...
206
207
208
209
210
211
212
213
214
215
  
  	return snprintf(buf, PAGE_SIZE, "%lu
  ", (unsigned long)mtd->oobsize);
  
  }
  static DEVICE_ATTR(oobsize, S_IRUGO, mtd_oobsize_show, NULL);
  
  static ssize_t mtd_numeraseregions_show(struct device *dev,
  		struct device_attribute *attr, char *buf)
  {
d5de20a9a   Artem Bityutskiy   mtd: kill dev_to_...
216
  	struct mtd_info *mtd = dev_get_drvdata(dev);
694bb7fc1   Kevin Cernekee   [MTD] driver mode...
217
218
219
220
221
222
223
224
225
226
227
  
  	return snprintf(buf, PAGE_SIZE, "%u
  ", mtd->numeraseregions);
  
  }
  static DEVICE_ATTR(numeraseregions, S_IRUGO, mtd_numeraseregions_show,
  	NULL);
  
  static ssize_t mtd_name_show(struct device *dev,
  		struct device_attribute *attr, char *buf)
  {
d5de20a9a   Artem Bityutskiy   mtd: kill dev_to_...
228
  	struct mtd_info *mtd = dev_get_drvdata(dev);
694bb7fc1   Kevin Cernekee   [MTD] driver mode...
229
230
231
232
233
234
  
  	return snprintf(buf, PAGE_SIZE, "%s
  ", mtd->name);
  
  }
  static DEVICE_ATTR(name, S_IRUGO, mtd_name_show, NULL);
1f24b5a8e   David Brownell   [MTD] driver mode...
235

a9b672e82   Mike Dunn   mtd: expose ecc_s...
236
237
238
239
240
241
242
243
244
  static ssize_t mtd_ecc_strength_show(struct device *dev,
  				     struct device_attribute *attr, char *buf)
  {
  	struct mtd_info *mtd = dev_get_drvdata(dev);
  
  	return snprintf(buf, PAGE_SIZE, "%u
  ", mtd->ecc_strength);
  }
  static DEVICE_ATTR(ecc_strength, S_IRUGO, mtd_ecc_strength_show, NULL);
d062d4ede   Mike Dunn   mtd: bitflip_thre...
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
  static ssize_t mtd_bitflip_threshold_show(struct device *dev,
  					  struct device_attribute *attr,
  					  char *buf)
  {
  	struct mtd_info *mtd = dev_get_drvdata(dev);
  
  	return snprintf(buf, PAGE_SIZE, "%u
  ", mtd->bitflip_threshold);
  }
  
  static ssize_t mtd_bitflip_threshold_store(struct device *dev,
  					   struct device_attribute *attr,
  					   const char *buf, size_t count)
  {
  	struct mtd_info *mtd = dev_get_drvdata(dev);
  	unsigned int bitflip_threshold;
  	int retval;
  
  	retval = kstrtouint(buf, 0, &bitflip_threshold);
  	if (retval)
  		return retval;
  
  	mtd->bitflip_threshold = bitflip_threshold;
  	return count;
  }
  static DEVICE_ATTR(bitflip_threshold, S_IRUGO | S_IWUSR,
  		   mtd_bitflip_threshold_show,
  		   mtd_bitflip_threshold_store);
bf977e3f1   Huang Shijie   mtd: add a new sy...
273
274
275
276
277
278
279
280
281
282
  static ssize_t mtd_ecc_step_size_show(struct device *dev,
  		struct device_attribute *attr, char *buf)
  {
  	struct mtd_info *mtd = dev_get_drvdata(dev);
  
  	return snprintf(buf, PAGE_SIZE, "%u
  ", mtd->ecc_step_size);
  
  }
  static DEVICE_ATTR(ecc_step_size, S_IRUGO, mtd_ecc_step_size_show, NULL);
990a3af0c   Ezequiel Garcia   mtd: Add sysfs at...
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
  static ssize_t mtd_ecc_stats_corrected_show(struct device *dev,
  		struct device_attribute *attr, char *buf)
  {
  	struct mtd_info *mtd = dev_get_drvdata(dev);
  	struct mtd_ecc_stats *ecc_stats = &mtd->ecc_stats;
  
  	return snprintf(buf, PAGE_SIZE, "%u
  ", ecc_stats->corrected);
  }
  static DEVICE_ATTR(corrected_bits, S_IRUGO,
  		   mtd_ecc_stats_corrected_show, NULL);
  
  static ssize_t mtd_ecc_stats_errors_show(struct device *dev,
  		struct device_attribute *attr, char *buf)
  {
  	struct mtd_info *mtd = dev_get_drvdata(dev);
  	struct mtd_ecc_stats *ecc_stats = &mtd->ecc_stats;
  
  	return snprintf(buf, PAGE_SIZE, "%u
  ", ecc_stats->failed);
  }
  static DEVICE_ATTR(ecc_failures, S_IRUGO, mtd_ecc_stats_errors_show, NULL);
  
  static ssize_t mtd_badblocks_show(struct device *dev,
  		struct device_attribute *attr, char *buf)
  {
  	struct mtd_info *mtd = dev_get_drvdata(dev);
  	struct mtd_ecc_stats *ecc_stats = &mtd->ecc_stats;
  
  	return snprintf(buf, PAGE_SIZE, "%u
  ", ecc_stats->badblocks);
  }
  static DEVICE_ATTR(bad_blocks, S_IRUGO, mtd_badblocks_show, NULL);
  
  static ssize_t mtd_bbtblocks_show(struct device *dev,
  		struct device_attribute *attr, char *buf)
  {
  	struct mtd_info *mtd = dev_get_drvdata(dev);
  	struct mtd_ecc_stats *ecc_stats = &mtd->ecc_stats;
  
  	return snprintf(buf, PAGE_SIZE, "%u
  ", ecc_stats->bbtblocks);
  }
  static DEVICE_ATTR(bbt_blocks, S_IRUGO, mtd_bbtblocks_show, NULL);
1f24b5a8e   David Brownell   [MTD] driver mode...
327
  static struct attribute *mtd_attrs[] = {
694bb7fc1   Kevin Cernekee   [MTD] driver mode...
328
329
330
331
332
  	&dev_attr_type.attr,
  	&dev_attr_flags.attr,
  	&dev_attr_size.attr,
  	&dev_attr_erasesize.attr,
  	&dev_attr_writesize.attr,
e76935489   Artem Bityutskiy   mtd: expose subpa...
333
  	&dev_attr_subpagesize.attr,
694bb7fc1   Kevin Cernekee   [MTD] driver mode...
334
335
336
  	&dev_attr_oobsize.attr,
  	&dev_attr_numeraseregions.attr,
  	&dev_attr_name.attr,
a9b672e82   Mike Dunn   mtd: expose ecc_s...
337
  	&dev_attr_ecc_strength.attr,
bf977e3f1   Huang Shijie   mtd: add a new sy...
338
  	&dev_attr_ecc_step_size.attr,
990a3af0c   Ezequiel Garcia   mtd: Add sysfs at...
339
340
341
342
  	&dev_attr_corrected_bits.attr,
  	&dev_attr_ecc_failures.attr,
  	&dev_attr_bad_blocks.attr,
  	&dev_attr_bbt_blocks.attr,
d062d4ede   Mike Dunn   mtd: bitflip_thre...
343
  	&dev_attr_bitflip_threshold.attr,
1f24b5a8e   David Brownell   [MTD] driver mode...
344
345
  	NULL,
  };
54c738f69   Axel Lin   mtd: convert to u...
346
  ATTRIBUTE_GROUPS(mtd);
1f24b5a8e   David Brownell   [MTD] driver mode...
347
348
349
350
351
352
  
  static struct device_type mtd_devtype = {
  	.name		= "mtd",
  	.groups		= mtd_groups,
  	.release	= mtd_release,
  };
b4caecd48   Christoph Hellwig   fs: introduce f_o...
353
354
355
356
357
358
359
360
361
362
363
364
365
366
  #ifndef CONFIG_MMU
  unsigned mtd_mmap_capabilities(struct mtd_info *mtd)
  {
  	switch (mtd->type) {
  	case MTD_RAM:
  		return NOMMU_MAP_COPY | NOMMU_MAP_DIRECT | NOMMU_MAP_EXEC |
  			NOMMU_MAP_READ | NOMMU_MAP_WRITE;
  	case MTD_ROM:
  		return NOMMU_MAP_COPY | NOMMU_MAP_DIRECT | NOMMU_MAP_EXEC |
  			NOMMU_MAP_READ;
  	default:
  		return NOMMU_MAP_COPY;
  	}
  }
706a4e5a1   Arnd Bergmann   mtd: export new m...
367
  EXPORT_SYMBOL_GPL(mtd_mmap_capabilities);
b4caecd48   Christoph Hellwig   fs: introduce f_o...
368
  #endif
3efe41be2   Brian Norris   mtd: implement co...
369
370
371
372
373
374
375
376
377
378
  static int mtd_reboot_notifier(struct notifier_block *n, unsigned long state,
  			       void *cmd)
  {
  	struct mtd_info *mtd;
  
  	mtd = container_of(n, struct mtd_info, reboot_notifier);
  	mtd->_reboot(mtd);
  
  	return NOTIFY_DONE;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
379
  /**
477b0229a   Boris Brezillon   mtd: introduce th...
380
381
382
383
384
385
386
387
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
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
   * mtd_wunit_to_pairing_info - get pairing information of a wunit
   * @mtd: pointer to new MTD device info structure
   * @wunit: write unit we are interested in
   * @info: returned pairing information
   *
   * Retrieve pairing information associated to the wunit.
   * This is mainly useful when dealing with MLC/TLC NANDs where pages can be
   * paired together, and where programming a page may influence the page it is
   * paired with.
   * The notion of page is replaced by the term wunit (write-unit) to stay
   * consistent with the ->writesize field.
   *
   * The @wunit argument can be extracted from an absolute offset using
   * mtd_offset_to_wunit(). @info is filled with the pairing information attached
   * to @wunit.
   *
   * From the pairing info the MTD user can find all the wunits paired with
   * @wunit using the following loop:
   *
   * for (i = 0; i < mtd_pairing_groups(mtd); i++) {
   *	info.pair = i;
   *	mtd_pairing_info_to_wunit(mtd, &info);
   *	...
   * }
   */
  int mtd_wunit_to_pairing_info(struct mtd_info *mtd, int wunit,
  			      struct mtd_pairing_info *info)
  {
  	int npairs = mtd_wunit_per_eb(mtd) / mtd_pairing_groups(mtd);
  
  	if (wunit < 0 || wunit >= npairs)
  		return -EINVAL;
  
  	if (mtd->pairing && mtd->pairing->get_info)
  		return mtd->pairing->get_info(mtd, wunit, info);
  
  	info->group = 0;
  	info->pair = wunit;
  
  	return 0;
  }
  EXPORT_SYMBOL_GPL(mtd_wunit_to_pairing_info);
  
  /**
   * mtd_wunit_to_pairing_info - get wunit from pairing information
   * @mtd: pointer to new MTD device info structure
   * @info: pairing information struct
   *
   * Returns a positive number representing the wunit associated to the info
   * struct, or a negative error code.
   *
   * This is the reverse of mtd_wunit_to_pairing_info(), and can help one to
   * iterate over all wunits of a given pair (see mtd_wunit_to_pairing_info()
   * doc).
   *
   * It can also be used to only program the first page of each pair (i.e.
   * page attached to group 0), which allows one to use an MLC NAND in
   * software-emulated SLC mode:
   *
   * info.group = 0;
   * npairs = mtd_wunit_per_eb(mtd) / mtd_pairing_groups(mtd);
   * for (info.pair = 0; info.pair < npairs; info.pair++) {
   *	wunit = mtd_pairing_info_to_wunit(mtd, &info);
   *	mtd_write(mtd, mtd_wunit_to_offset(mtd, blkoffs, wunit),
   *		  mtd->writesize, &retlen, buf + (i * mtd->writesize));
   * }
   */
  int mtd_pairing_info_to_wunit(struct mtd_info *mtd,
  			      const struct mtd_pairing_info *info)
  {
  	int ngroups = mtd_pairing_groups(mtd);
  	int npairs = mtd_wunit_per_eb(mtd) / ngroups;
  
  	if (!info || info->pair < 0 || info->pair >= npairs ||
  	    info->group < 0 || info->group >= ngroups)
  		return -EINVAL;
  
  	if (mtd->pairing && mtd->pairing->get_wunit)
  		return mtd->pairing->get_wunit(mtd, info);
  
  	return info->pair;
  }
  EXPORT_SYMBOL_GPL(mtd_pairing_info_to_wunit);
  
  /**
   * mtd_pairing_groups - get the number of pairing groups
   * @mtd: pointer to new MTD device info structure
   *
   * Returns the number of pairing groups.
   *
   * This number is usually equal to the number of bits exposed by a single
   * cell, and can be used in conjunction with mtd_pairing_info_to_wunit()
   * to iterate over all pages of a given pair.
   */
  int mtd_pairing_groups(struct mtd_info *mtd)
  {
  	if (!mtd->pairing || !mtd->pairing->ngroups)
  		return 1;
  
  	return mtd->pairing->ngroups;
  }
  EXPORT_SYMBOL_GPL(mtd_pairing_groups);
  
  /**
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
484
485
486
487
488
   *	add_mtd_device - register an MTD device
   *	@mtd: pointer to new MTD device info structure
   *
   *	Add a device to the list of MTD devices present in the system, and
   *	notify each currently active MTD 'user' of its arrival. Returns
57dd990c5   Brian Norris   mtd: propagate er...
489
   *	zero on success or non-zero on failure.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
490
491
492
493
   */
  
  int add_mtd_device(struct mtd_info *mtd)
  {
b520e412f   Ben Hutchings   mtd: Replace stat...
494
495
  	struct mtd_notifier *not;
  	int i, error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
496

be0dbff8b   Brian Norris   mtd: warn when re...
497
498
499
500
501
  	/*
  	 * May occur, for instance, on buggy drivers which call
  	 * mtd_device_parse_register() multiple times on the same master MTD,
  	 * especially with CONFIG_MTD_PARTITIONED_MASTER=y.
  	 */
fa06052d6   Jan Kara   mtd: Convert to d...
502
503
  	if (WARN_ONCE(mtd->dev.type, "MTD already registered
  "))
be0dbff8b   Brian Norris   mtd: warn when re...
504
  		return -EEXIST;
783ed81ff   Artem B. Bityutskiy   [MTD] assume mtd-...
505
  	BUG_ON(mtd->writesize == 0);
48b192686   Ingo Molnar   [PATCH] sem2mutex...
506
  	mutex_lock(&mtd_table_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
507

589e9c4da   Tejun Heo   mtd: convert to i...
508
  	i = idr_alloc(&mtd_idr, mtd, 0, 0, GFP_KERNEL);
57dd990c5   Brian Norris   mtd: propagate er...
509
510
  	if (i < 0) {
  		error = i;
b520e412f   Ben Hutchings   mtd: Replace stat...
511
  		goto fail_locked;
57dd990c5   Brian Norris   mtd: propagate er...
512
  	}
1f24b5a8e   David Brownell   [MTD] driver mode...
513

b520e412f   Ben Hutchings   mtd: Replace stat...
514
515
  	mtd->index = i;
  	mtd->usecount = 0;
d062d4ede   Mike Dunn   mtd: bitflip_thre...
516
517
518
  	/* default value if not set by driver */
  	if (mtd->bitflip_threshold == 0)
  		mtd->bitflip_threshold = mtd->ecc_strength;
b520e412f   Ben Hutchings   mtd: Replace stat...
519
520
521
522
523
524
525
526
527
528
529
530
531
532
  	if (is_power_of_2(mtd->erasesize))
  		mtd->erasesize_shift = ffs(mtd->erasesize) - 1;
  	else
  		mtd->erasesize_shift = 0;
  
  	if (is_power_of_2(mtd->writesize))
  		mtd->writesize_shift = ffs(mtd->writesize) - 1;
  	else
  		mtd->writesize_shift = 0;
  
  	mtd->erasesize_mask = (1 << mtd->erasesize_shift) - 1;
  	mtd->writesize_mask = (1 << mtd->writesize_shift) - 1;
  
  	/* Some chips always power up locked. Unlock them now */
381345652   Artem Bityutskiy   mtd: do not use m...
533
534
535
  	if ((mtd->flags & MTD_WRITEABLE) && (mtd->flags & MTD_POWERUP_LOCK)) {
  		error = mtd_unlock(mtd, 0, mtd->size);
  		if (error && error != -EOPNOTSUPP)
b520e412f   Ben Hutchings   mtd: Replace stat...
536
537
538
539
  			printk(KERN_WARNING
  			       "%s: unlock failed, writes may not work
  ",
  			       mtd->name);
57dd990c5   Brian Norris   mtd: propagate er...
540
541
  		/* Ignore unlock failures? */
  		error = 0;
b520e412f   Ben Hutchings   mtd: Replace stat...
542
543
544
  	}
  
  	/* Caller should have set dev.parent to match the
260e89a6e   Frans Klaver   mtd: core: tone d...
545
  	 * physical device, if appropriate.
b520e412f   Ben Hutchings   mtd: Replace stat...
546
547
548
549
550
551
  	 */
  	mtd->dev.type = &mtd_devtype;
  	mtd->dev.class = &mtd_class;
  	mtd->dev.devt = MTD_DEVT(i);
  	dev_set_name(&mtd->dev, "mtd%d", i);
  	dev_set_drvdata(&mtd->dev, mtd);
215a02fd3   Brian Norris   mtd: grab a refer...
552
  	of_node_get(mtd_get_of_node(mtd));
57dd990c5   Brian Norris   mtd: propagate er...
553
554
  	error = device_register(&mtd->dev);
  	if (error)
b520e412f   Ben Hutchings   mtd: Replace stat...
555
  		goto fail_added;
5e4721283   Brian Norris   mtd: remove dead ...
556
557
  	device_create(&mtd_class, mtd->dev.parent, MTD_DEVT(i) + 1, NULL,
  		      "mtd%dro", i);
b520e412f   Ben Hutchings   mtd: Replace stat...
558

289c05222   Brian Norris   mtd: replace DEBU...
559
560
  	pr_debug("mtd: Giving out device %d to %s
  ", i, mtd->name);
b520e412f   Ben Hutchings   mtd: Replace stat...
561
562
563
564
565
566
567
568
569
570
571
572
  	/* No need to get a refcount on the module containing
  	   the notifier, since we hold the mtd_table_mutex */
  	list_for_each_entry(not, &mtd_notifiers, list)
  		not->add(mtd);
  
  	mutex_unlock(&mtd_table_mutex);
  	/* We _know_ we aren't being removed, because
  	   our caller is still holding us here. So none
  	   of this try_ nonsense, and no bitching about it
  	   either. :) */
  	__module_get(THIS_MODULE);
  	return 0;
97894cda5   Thomas Gleixner   [MTD] core: Clean...
573

b520e412f   Ben Hutchings   mtd: Replace stat...
574
  fail_added:
215a02fd3   Brian Norris   mtd: grab a refer...
575
  	of_node_put(mtd_get_of_node(mtd));
b520e412f   Ben Hutchings   mtd: Replace stat...
576
577
  	idr_remove(&mtd_idr, i);
  fail_locked:
48b192686   Ingo Molnar   [PATCH] sem2mutex...
578
  	mutex_unlock(&mtd_table_mutex);
57dd990c5   Brian Norris   mtd: propagate er...
579
  	return error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
580
581
582
583
584
585
586
587
588
589
590
  }
  
  /**
   *	del_mtd_device - unregister an MTD device
   *	@mtd: pointer to MTD device info structure
   *
   *	Remove a device from the list of MTD devices present in the system,
   *	and notify each currently active MTD 'user' of its departure.
   *	Returns zero on success or 1 on failure, which currently will happen
   *	if the requested device does not appear to be present in the list.
   */
eea72d5fd   Jamie Iles   mtd: remove add_m...
591
  int del_mtd_device(struct mtd_info *mtd)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
592
593
  {
  	int ret;
75c0b84d4   Maxim Levitsky   mtd: call the rem...
594
  	struct mtd_notifier *not;
97894cda5   Thomas Gleixner   [MTD] core: Clean...
595

48b192686   Ingo Molnar   [PATCH] sem2mutex...
596
  	mutex_lock(&mtd_table_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
597

b520e412f   Ben Hutchings   mtd: Replace stat...
598
  	if (idr_find(&mtd_idr, mtd->index) != mtd) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
599
  		ret = -ENODEV;
75c0b84d4   Maxim Levitsky   mtd: call the rem...
600
601
602
603
604
605
606
607
608
  		goto out_error;
  	}
  
  	/* No need to get a refcount on the module containing
  		the notifier, since we hold the mtd_table_mutex */
  	list_for_each_entry(not, &mtd_notifiers, list)
  		not->remove(mtd);
  
  	if (mtd->usecount) {
97894cda5   Thomas Gleixner   [MTD] core: Clean...
609
610
  		printk(KERN_NOTICE "Removing MTD device #%d (%s) with use count %d
  ",
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
611
612
613
  		       mtd->index, mtd->name, mtd->usecount);
  		ret = -EBUSY;
  	} else {
694bb7fc1   Kevin Cernekee   [MTD] driver mode...
614
  		device_unregister(&mtd->dev);
b520e412f   Ben Hutchings   mtd: Replace stat...
615
  		idr_remove(&mtd_idr, mtd->index);
215a02fd3   Brian Norris   mtd: grab a refer...
616
  		of_node_put(mtd_get_of_node(mtd));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
617
618
619
620
  
  		module_put(THIS_MODULE);
  		ret = 0;
  	}
75c0b84d4   Maxim Levitsky   mtd: call the rem...
621
  out_error:
48b192686   Ingo Molnar   [PATCH] sem2mutex...
622
  	mutex_unlock(&mtd_table_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
623
624
  	return ret;
  }
727dc612c   Dan Ehrenberg   mtd: part: Create...
625
  static int mtd_add_device_partitions(struct mtd_info *mtd,
07fd2f871   Brian Norris   mtd: partitions: ...
626
  				     struct mtd_partitions *parts)
727dc612c   Dan Ehrenberg   mtd: part: Create...
627
  {
07fd2f871   Brian Norris   mtd: partitions: ...
628
629
  	const struct mtd_partition *real_parts = parts->parts;
  	int nbparts = parts->nr_parts;
727dc612c   Dan Ehrenberg   mtd: part: Create...
630
631
632
633
  	int ret;
  
  	if (nbparts == 0 || IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER)) {
  		ret = add_mtd_device(mtd);
57dd990c5   Brian Norris   mtd: propagate er...
634
635
  		if (ret)
  			return ret;
727dc612c   Dan Ehrenberg   mtd: part: Create...
636
637
638
639
640
641
642
643
644
645
646
  	}
  
  	if (nbparts > 0) {
  		ret = add_mtd_partitions(mtd, real_parts, nbparts);
  		if (ret && IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER))
  			del_mtd_device(mtd);
  		return ret;
  	}
  
  	return 0;
  }
472b444ee   Brian Norris   mtd: fix cmdlinep...
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
  /*
   * Set a few defaults based on the parent devices, if not provided by the
   * driver
   */
  static void mtd_set_dev_defaults(struct mtd_info *mtd)
  {
  	if (mtd->dev.parent) {
  		if (!mtd->owner && mtd->dev.parent->driver)
  			mtd->owner = mtd->dev.parent->driver->owner;
  		if (!mtd->name)
  			mtd->name = dev_name(mtd->dev.parent);
  	} else {
  		pr_debug("mtd device won't show a device symlink in sysfs
  ");
  	}
  }
727dc612c   Dan Ehrenberg   mtd: part: Create...
663

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
664
  /**
1c4c215cb   Dmitry Eremin-Solenikov   mtd: add new API ...
665
666
667
668
669
   * mtd_device_parse_register - parse partitions and register an MTD device.
   *
   * @mtd: the MTD device to register
   * @types: the list of MTD partition probes to try, see
   *         'parse_mtd_partitions()' for more information
c79753301   Dmitry Eremin-Solenikov   mtd: abstract las...
670
   * @parser_data: MTD partition parser-specific data
1c4c215cb   Dmitry Eremin-Solenikov   mtd: add new API ...
671
672
673
674
675
676
677
678
679
680
681
682
683
684
   * @parts: fallback partition information to register, if parsing fails;
   *         only valid if %nr_parts > %0
   * @nr_parts: the number of partitions in parts, if zero then the full
   *            MTD device is registered if no partition info is found
   *
   * This function aggregates MTD partitions parsing (done by
   * 'parse_mtd_partitions()') and MTD device and partitions registering. It
   * basically follows the most common pattern found in many MTD drivers:
   *
   * * It first tries to probe partitions on MTD device @mtd using parsers
   *   specified in @types (if @types is %NULL, then the default list of parsers
   *   is used, see 'parse_mtd_partitions()' for more information). If none are
   *   found this functions tries to fallback to information specified in
   *   @parts/@nr_parts.
92394b5c2   Brian Norris   mtd: spelling fixes
685
   * * If any partitioning info was found, this function registers the found
727dc612c   Dan Ehrenberg   mtd: part: Create...
686
687
   *   partitions. If the MTD_PARTITIONED_MASTER option is set, then the device
   *   as a whole is registered first.
1c4c215cb   Dmitry Eremin-Solenikov   mtd: add new API ...
688
689
690
691
692
   * * If no partitions were found this function just registers the MTD device
   *   @mtd and exits.
   *
   * Returns zero in case of success and a negative error code in case of failure.
   */
26a473462   Artem Bityutskiy   mtd: add 'const' ...
693
  int mtd_device_parse_register(struct mtd_info *mtd, const char * const *types,
c79753301   Dmitry Eremin-Solenikov   mtd: abstract las...
694
  			      struct mtd_part_parser_data *parser_data,
1c4c215cb   Dmitry Eremin-Solenikov   mtd: add new API ...
695
696
697
  			      const struct mtd_partition *parts,
  			      int nr_parts)
  {
07fd2f871   Brian Norris   mtd: partitions: ...
698
  	struct mtd_partitions parsed;
727dc612c   Dan Ehrenberg   mtd: part: Create...
699
  	int ret;
1c4c215cb   Dmitry Eremin-Solenikov   mtd: add new API ...
700

472b444ee   Brian Norris   mtd: fix cmdlinep...
701
  	mtd_set_dev_defaults(mtd);
07fd2f871   Brian Norris   mtd: partitions: ...
702
703
704
705
706
707
708
709
710
711
712
  	memset(&parsed, 0, sizeof(parsed));
  
  	ret = parse_mtd_partitions(mtd, types, &parsed, parser_data);
  	if ((ret < 0 || parsed.nr_parts == 0) && parts && nr_parts) {
  		/* Fall back to driver-provided partitions */
  		parsed = (struct mtd_partitions){
  			.parts		= parts,
  			.nr_parts	= nr_parts,
  		};
  	} else if (ret < 0) {
  		/* Didn't come up with parsed OR fallback partitions */
5a2415b07   Brian Norris   mtd: mtdpart: Do ...
713
714
715
716
  		pr_info("mtd: failed to find partitions; one or more parsers reports errors (%d)
  ",
  			ret);
  		/* Don't abort on errors; we can still use unpartitioned MTD */
07fd2f871   Brian Norris   mtd: partitions: ...
717
  		memset(&parsed, 0, sizeof(parsed));
3e00ed0e9   Brian Norris   mtd: fixup corner...
718
  	}
1c4c215cb   Dmitry Eremin-Solenikov   mtd: add new API ...
719

07fd2f871   Brian Norris   mtd: partitions: ...
720
  	ret = mtd_add_device_partitions(mtd, &parsed);
3e00ed0e9   Brian Norris   mtd: fixup corner...
721
722
  	if (ret)
  		goto out;
1c4c215cb   Dmitry Eremin-Solenikov   mtd: add new API ...
723

e1dd8641c   Niklas Cassel   mtd: avoid regist...
724
725
726
727
728
729
730
731
  	/*
  	 * FIXME: some drivers unfortunately call this function more than once.
  	 * So we have to check if we've already assigned the reboot notifier.
  	 *
  	 * Generally, we can make multiple calls work for most cases, but it
  	 * does cause problems with parse_mtd_partitions() above (e.g.,
  	 * cmdlineparts will register partitions more than once).
  	 */
f8479dd6a   Brian Norris   mtd: don't WARN a...
732
733
734
  	WARN_ONCE(mtd->_reboot && mtd->reboot_notifier.notifier_call,
  		  "MTD already registered
  ");
e1dd8641c   Niklas Cassel   mtd: avoid regist...
735
  	if (mtd->_reboot && !mtd->reboot_notifier.notifier_call) {
3efe41be2   Brian Norris   mtd: implement co...
736
737
738
  		mtd->reboot_notifier.notifier_call = mtd_reboot_notifier;
  		register_reboot_notifier(&mtd->reboot_notifier);
  	}
3e00ed0e9   Brian Norris   mtd: fixup corner...
739
  out:
c42c2710d   Brian Norris   mtd: partitions: ...
740
  	/* Cleanup any parsed partitions */
adc83bf88   Brian Norris   mtd: partitions: ...
741
  	mtd_part_parser_cleanup(&parsed);
727dc612c   Dan Ehrenberg   mtd: part: Create...
742
  	return ret;
1c4c215cb   Dmitry Eremin-Solenikov   mtd: add new API ...
743
744
745
746
  }
  EXPORT_SYMBOL_GPL(mtd_device_parse_register);
  
  /**
f5671ab3f   Jamie Iles   mtd: introduce mt...
747
748
749
750
751
752
753
754
   * mtd_device_unregister - unregister an existing MTD device.
   *
   * @master: the MTD device to unregister.  This will unregister both the master
   *          and any partitions if registered.
   */
  int mtd_device_unregister(struct mtd_info *master)
  {
  	int err;
3efe41be2   Brian Norris   mtd: implement co...
755
756
  	if (master->_reboot)
  		unregister_reboot_notifier(&master->reboot_notifier);
f5671ab3f   Jamie Iles   mtd: introduce mt...
757
758
759
760
761
762
763
764
765
766
767
768
  	err = del_mtd_partitions(master);
  	if (err)
  		return err;
  
  	if (!device_is_registered(&master->dev))
  		return 0;
  
  	return del_mtd_device(master);
  }
  EXPORT_SYMBOL_GPL(mtd_device_unregister);
  
  /**
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
769
770
771
772
773
774
775
   *	register_mtd_user - register a 'user' of MTD devices.
   *	@new: pointer to notifier info structure
   *
   *	Registers a pair of callbacks function to be called upon addition
   *	or removal of MTD devices. Causes the 'add' callback to be immediately
   *	invoked for each MTD device currently present in the system.
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
776
777
  void register_mtd_user (struct mtd_notifier *new)
  {
f1332ba2f   Ben Hutchings   mtd: Introduce an...
778
  	struct mtd_info *mtd;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
779

48b192686   Ingo Molnar   [PATCH] sem2mutex...
780
  	mutex_lock(&mtd_table_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
781
782
  
  	list_add(&new->list, &mtd_notifiers);
d5ca51292   Wanlong Gao   mtd:fix the bad f...
783
  	__module_get(THIS_MODULE);
97894cda5   Thomas Gleixner   [MTD] core: Clean...
784

f1332ba2f   Ben Hutchings   mtd: Introduce an...
785
786
  	mtd_for_each_device(mtd)
  		new->add(mtd);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
787

48b192686   Ingo Molnar   [PATCH] sem2mutex...
788
  	mutex_unlock(&mtd_table_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
789
  }
33c87b4a2   Artem Bityutskiy   mtd: mtdcore: exp...
790
  EXPORT_SYMBOL_GPL(register_mtd_user);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
791
792
  
  /**
494507958   Artem B. Bityuckiy   [MTD] Fix unregis...
793
794
   *	unregister_mtd_user - unregister a 'user' of MTD devices.
   *	@old: pointer to notifier info structure
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
795
796
797
798
799
800
   *
   *	Removes a callback function pair from the list of 'users' to be
   *	notified upon addition or removal of MTD devices. Causes the
   *	'remove' callback to be immediately invoked for each MTD device
   *	currently present in the system.
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
801
802
  int unregister_mtd_user (struct mtd_notifier *old)
  {
f1332ba2f   Ben Hutchings   mtd: Introduce an...
803
  	struct mtd_info *mtd;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
804

48b192686   Ingo Molnar   [PATCH] sem2mutex...
805
  	mutex_lock(&mtd_table_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
806
807
  
  	module_put(THIS_MODULE);
f1332ba2f   Ben Hutchings   mtd: Introduce an...
808
809
  	mtd_for_each_device(mtd)
  		old->remove(mtd);
97894cda5   Thomas Gleixner   [MTD] core: Clean...
810

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
811
  	list_del(&old->list);
48b192686   Ingo Molnar   [PATCH] sem2mutex...
812
  	mutex_unlock(&mtd_table_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
813
814
  	return 0;
  }
33c87b4a2   Artem Bityutskiy   mtd: mtdcore: exp...
815
  EXPORT_SYMBOL_GPL(unregister_mtd_user);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
816
817
818
819
820
821
822
823
824
  
  /**
   *	get_mtd_device - obtain a validated handle for an MTD device
   *	@mtd: last known address of the required MTD device
   *	@num: internal device number of the required MTD device
   *
   *	Given a number and NULL address, return the num'th entry in the device
   *	table, if any.	Given an address and num == -1, search the device table
   *	for a device with that address and return if it's still present. Given
9c74034f8   Artem Bityutskiy   [MTD] return erro...
825
826
   *	both, return the num'th driver only if its address matches. Return
   *	error code if not.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
827
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
828
829
  struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num)
  {
f1332ba2f   Ben Hutchings   mtd: Introduce an...
830
831
  	struct mtd_info *ret = NULL, *other;
  	int err = -ENODEV;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
832

48b192686   Ingo Molnar   [PATCH] sem2mutex...
833
  	mutex_lock(&mtd_table_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
834
835
  
  	if (num == -1) {
f1332ba2f   Ben Hutchings   mtd: Introduce an...
836
837
838
839
840
841
  		mtd_for_each_device(other) {
  			if (other == mtd) {
  				ret = mtd;
  				break;
  			}
  		}
b520e412f   Ben Hutchings   mtd: Replace stat...
842
843
  	} else if (num >= 0) {
  		ret = idr_find(&mtd_idr, num);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
844
845
846
  		if (mtd && mtd != ret)
  			ret = NULL;
  	}
3bd456576   Maxim Levitsky   mtd: create unloc...
847
848
849
  	if (!ret) {
  		ret = ERR_PTR(err);
  		goto out;
9fe912cea   Artem Bityutskiy   [MTD] add get and...
850
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
851

3bd456576   Maxim Levitsky   mtd: create unloc...
852
853
854
855
  	err = __get_mtd_device(ret);
  	if (err)
  		ret = ERR_PTR(err);
  out:
9c74034f8   Artem Bityutskiy   [MTD] return erro...
856
857
  	mutex_unlock(&mtd_table_mutex);
  	return ret;
3bd456576   Maxim Levitsky   mtd: create unloc...
858
  }
33c87b4a2   Artem Bityutskiy   mtd: mtdcore: exp...
859
  EXPORT_SYMBOL_GPL(get_mtd_device);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
860

3bd456576   Maxim Levitsky   mtd: create unloc...
861
862
863
864
865
866
867
  
  int __get_mtd_device(struct mtd_info *mtd)
  {
  	int err;
  
  	if (!try_module_get(mtd->owner))
  		return -ENODEV;
3c3c10bba   Artem Bityutskiy   mtd: add leading ...
868
869
  	if (mtd->_get_device) {
  		err = mtd->_get_device(mtd);
3bd456576   Maxim Levitsky   mtd: create unloc...
870
871
872
873
874
875
876
877
  
  		if (err) {
  			module_put(mtd->owner);
  			return err;
  		}
  	}
  	mtd->usecount++;
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
878
  }
33c87b4a2   Artem Bityutskiy   mtd: mtdcore: exp...
879
  EXPORT_SYMBOL_GPL(__get_mtd_device);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
880

7799308f3   Artem Bityutskiy   [MTD] add get_mtd...
881
882
883
884
885
886
887
888
  /**
   *	get_mtd_device_nm - obtain a validated handle for an MTD device by
   *	device name
   *	@name: MTD device name to open
   *
   * 	This function returns MTD device description structure in case of
   * 	success and an error code in case of failure.
   */
7799308f3   Artem Bityutskiy   [MTD] add get_mtd...
889
890
  struct mtd_info *get_mtd_device_nm(const char *name)
  {
f1332ba2f   Ben Hutchings   mtd: Introduce an...
891
892
  	int err = -ENODEV;
  	struct mtd_info *mtd = NULL, *other;
7799308f3   Artem Bityutskiy   [MTD] add get_mtd...
893
894
  
  	mutex_lock(&mtd_table_mutex);
f1332ba2f   Ben Hutchings   mtd: Introduce an...
895
896
897
  	mtd_for_each_device(other) {
  		if (!strcmp(name, other->name)) {
  			mtd = other;
7799308f3   Artem Bityutskiy   [MTD] add get_mtd...
898
899
900
  			break;
  		}
  	}
9fe912cea   Artem Bityutskiy   [MTD] add get and...
901
  	if (!mtd)
7799308f3   Artem Bityutskiy   [MTD] add get_mtd...
902
  		goto out_unlock;
52534f2db   Wanlong Gao   mtd: simplify get...
903
904
  	err = __get_mtd_device(mtd);
  	if (err)
7799308f3   Artem Bityutskiy   [MTD] add get_mtd...
905
  		goto out_unlock;
9fe912cea   Artem Bityutskiy   [MTD] add get and...
906
907
  	mutex_unlock(&mtd_table_mutex);
  	return mtd;
7799308f3   Artem Bityutskiy   [MTD] add get_mtd...
908
909
910
  
  out_unlock:
  	mutex_unlock(&mtd_table_mutex);
9fe912cea   Artem Bityutskiy   [MTD] add get and...
911
  	return ERR_PTR(err);
7799308f3   Artem Bityutskiy   [MTD] add get_mtd...
912
  }
33c87b4a2   Artem Bityutskiy   mtd: mtdcore: exp...
913
  EXPORT_SYMBOL_GPL(get_mtd_device_nm);
7799308f3   Artem Bityutskiy   [MTD] add get_mtd...
914

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
915
916
  void put_mtd_device(struct mtd_info *mtd)
  {
48b192686   Ingo Molnar   [PATCH] sem2mutex...
917
  	mutex_lock(&mtd_table_mutex);
3bd456576   Maxim Levitsky   mtd: create unloc...
918
919
920
921
  	__put_mtd_device(mtd);
  	mutex_unlock(&mtd_table_mutex);
  
  }
33c87b4a2   Artem Bityutskiy   mtd: mtdcore: exp...
922
  EXPORT_SYMBOL_GPL(put_mtd_device);
3bd456576   Maxim Levitsky   mtd: create unloc...
923
924
925
926
927
  
  void __put_mtd_device(struct mtd_info *mtd)
  {
  	--mtd->usecount;
  	BUG_ON(mtd->usecount < 0);
3c3c10bba   Artem Bityutskiy   mtd: add leading ...
928
929
  	if (mtd->_put_device)
  		mtd->_put_device(mtd);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
930
931
932
  
  	module_put(mtd->owner);
  }
33c87b4a2   Artem Bityutskiy   mtd: mtdcore: exp...
933
  EXPORT_SYMBOL_GPL(__put_mtd_device);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
934

52b020317   Artem Bityutskiy   mtd: clean-up the...
935
  /*
8273a0c91   Artem Bityutskiy   mtd: add offset a...
936
937
938
939
940
941
942
943
   * Erase is an asynchronous operation.  Device drivers are supposed
   * to call instr->callback() whenever the operation completes, even
   * if it completes with a failure.
   * Callers are supposed to pass a callback function and wait for it
   * to be called before writing to the block.
   */
  int mtd_erase(struct mtd_info *mtd, struct erase_info *instr)
  {
0c2b4e214   Brian Norris   mtd: correct uppe...
944
  	if (instr->addr >= mtd->size || instr->len > mtd->size - instr->addr)
8273a0c91   Artem Bityutskiy   mtd: add offset a...
945
  		return -EINVAL;
664addc24   Artem Bityutskiy   mtd: remove R/O c...
946
947
  	if (!(mtd->flags & MTD_WRITEABLE))
  		return -EROFS;
3b27dac03   Shmulik Ladkani   mtd: unify initia...
948
  	instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN;
bcb1d2387   Artem Bityutskiy   mtd: move zero le...
949
950
951
952
953
  	if (!instr->len) {
  		instr->state = MTD_ERASE_DONE;
  		mtd_erase_callback(instr);
  		return 0;
  	}
fea728c09   Ezequiel Garcia   mtd: Hook I/O act...
954
  	ledtrig_mtd_activity();
8273a0c91   Artem Bityutskiy   mtd: add offset a...
955
956
957
958
959
960
961
962
963
964
965
  	return mtd->_erase(mtd, instr);
  }
  EXPORT_SYMBOL_GPL(mtd_erase);
  
  /*
   * This stuff for eXecute-In-Place. phys is optional and may be set to NULL.
   */
  int mtd_point(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen,
  	      void **virt, resource_size_t *phys)
  {
  	*retlen = 0;
0dd5235f5   Artem Bityutskiy   mtd: harmonize mt...
966
967
968
  	*virt = NULL;
  	if (phys)
  		*phys = 0;
8273a0c91   Artem Bityutskiy   mtd: add offset a...
969
970
  	if (!mtd->_point)
  		return -EOPNOTSUPP;
0c2b4e214   Brian Norris   mtd: correct uppe...
971
  	if (from < 0 || from >= mtd->size || len > mtd->size - from)
8273a0c91   Artem Bityutskiy   mtd: add offset a...
972
  		return -EINVAL;
bcb1d2387   Artem Bityutskiy   mtd: move zero le...
973
974
  	if (!len)
  		return 0;
8273a0c91   Artem Bityutskiy   mtd: add offset a...
975
976
977
978
979
980
981
982
983
  	return mtd->_point(mtd, from, len, retlen, virt, phys);
  }
  EXPORT_SYMBOL_GPL(mtd_point);
  
  /* We probably shouldn't allow XIP if the unpoint isn't a NULL */
  int mtd_unpoint(struct mtd_info *mtd, loff_t from, size_t len)
  {
  	if (!mtd->_point)
  		return -EOPNOTSUPP;
0c2b4e214   Brian Norris   mtd: correct uppe...
984
  	if (from < 0 || from >= mtd->size || len > mtd->size - from)
8273a0c91   Artem Bityutskiy   mtd: add offset a...
985
  		return -EINVAL;
bcb1d2387   Artem Bityutskiy   mtd: move zero le...
986
987
  	if (!len)
  		return 0;
8273a0c91   Artem Bityutskiy   mtd: add offset a...
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
  	return mtd->_unpoint(mtd, from, len);
  }
  EXPORT_SYMBOL_GPL(mtd_unpoint);
  
  /*
   * Allow NOMMU mmap() to directly map the device (if not NULL)
   * - return the address to which the offset maps
   * - return -ENOSYS to indicate refusal to do the mapping
   */
  unsigned long mtd_get_unmapped_area(struct mtd_info *mtd, unsigned long len,
  				    unsigned long offset, unsigned long flags)
  {
  	if (!mtd->_get_unmapped_area)
  		return -EOPNOTSUPP;
0c2b4e214   Brian Norris   mtd: correct uppe...
1002
  	if (offset >= mtd->size || len > mtd->size - offset)
8273a0c91   Artem Bityutskiy   mtd: add offset a...
1003
1004
1005
1006
1007
1008
1009
1010
  		return -EINVAL;
  	return mtd->_get_unmapped_area(mtd, len, offset, flags);
  }
  EXPORT_SYMBOL_GPL(mtd_get_unmapped_area);
  
  int mtd_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen,
  	     u_char *buf)
  {
edbc4540e   Mike Dunn   mtd: driver _read...
1011
  	int ret_code;
834247ec7   Artem Bityutskiy   mtd: remove retle...
1012
  	*retlen = 0;
0c2b4e214   Brian Norris   mtd: correct uppe...
1013
  	if (from < 0 || from >= mtd->size || len > mtd->size - from)
8273a0c91   Artem Bityutskiy   mtd: add offset a...
1014
  		return -EINVAL;
bcb1d2387   Artem Bityutskiy   mtd: move zero le...
1015
1016
  	if (!len)
  		return 0;
edbc4540e   Mike Dunn   mtd: driver _read...
1017

fea728c09   Ezequiel Garcia   mtd: Hook I/O act...
1018
  	ledtrig_mtd_activity();
edbc4540e   Mike Dunn   mtd: driver _read...
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
  	/*
  	 * In the absence of an error, drivers return a non-negative integer
  	 * representing the maximum number of bitflips that were corrected on
  	 * any one ecc region (if applicable; zero otherwise).
  	 */
  	ret_code = mtd->_read(mtd, from, len, retlen, buf);
  	if (unlikely(ret_code < 0))
  		return ret_code;
  	if (mtd->ecc_strength == 0)
  		return 0;	/* device lacks ecc */
  	return ret_code >= mtd->bitflip_threshold ? -EUCLEAN : 0;
8273a0c91   Artem Bityutskiy   mtd: add offset a...
1030
1031
1032
1033
1034
1035
1036
  }
  EXPORT_SYMBOL_GPL(mtd_read);
  
  int mtd_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen,
  	      const u_char *buf)
  {
  	*retlen = 0;
0c2b4e214   Brian Norris   mtd: correct uppe...
1037
  	if (to < 0 || to >= mtd->size || len > mtd->size - to)
8273a0c91   Artem Bityutskiy   mtd: add offset a...
1038
  		return -EINVAL;
664addc24   Artem Bityutskiy   mtd: remove R/O c...
1039
1040
  	if (!mtd->_write || !(mtd->flags & MTD_WRITEABLE))
  		return -EROFS;
bcb1d2387   Artem Bityutskiy   mtd: move zero le...
1041
1042
  	if (!len)
  		return 0;
fea728c09   Ezequiel Garcia   mtd: Hook I/O act...
1043
  	ledtrig_mtd_activity();
8273a0c91   Artem Bityutskiy   mtd: add offset a...
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
  	return mtd->_write(mtd, to, len, retlen, buf);
  }
  EXPORT_SYMBOL_GPL(mtd_write);
  
  /*
   * In blackbox flight recorder like scenarios we want to make successful writes
   * in interrupt context. panic_write() is only intended to be called when its
   * known the kernel is about to panic and we need the write to succeed. Since
   * the kernel is not going to be running for much longer, this function can
   * break locks and delay to ensure the write succeeds (but not sleep).
   */
  int mtd_panic_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen,
  		    const u_char *buf)
  {
  	*retlen = 0;
  	if (!mtd->_panic_write)
  		return -EOPNOTSUPP;
0c2b4e214   Brian Norris   mtd: correct uppe...
1061
  	if (to < 0 || to >= mtd->size || len > mtd->size - to)
8273a0c91   Artem Bityutskiy   mtd: add offset a...
1062
  		return -EINVAL;
664addc24   Artem Bityutskiy   mtd: remove R/O c...
1063
1064
  	if (!(mtd->flags & MTD_WRITEABLE))
  		return -EROFS;
bcb1d2387   Artem Bityutskiy   mtd: move zero le...
1065
1066
  	if (!len)
  		return 0;
8273a0c91   Artem Bityutskiy   mtd: add offset a...
1067
1068
1069
  	return mtd->_panic_write(mtd, to, len, retlen, buf);
  }
  EXPORT_SYMBOL_GPL(mtd_panic_write);
d2d48480d   Brian Norris   mtd: move mtd_rea...
1070
1071
  int mtd_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops)
  {
e47f68587   Brian Norris   mtd: check for ma...
1072
  	int ret_code;
d2d48480d   Brian Norris   mtd: move mtd_rea...
1073
1074
1075
  	ops->retlen = ops->oobretlen = 0;
  	if (!mtd->_read_oob)
  		return -EOPNOTSUPP;
fea728c09   Ezequiel Garcia   mtd: Hook I/O act...
1076
1077
  
  	ledtrig_mtd_activity();
e47f68587   Brian Norris   mtd: check for ma...
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
  	/*
  	 * In cases where ops->datbuf != NULL, mtd->_read_oob() has semantics
  	 * similar to mtd->_read(), returning a non-negative integer
  	 * representing max bitflips. In other cases, mtd->_read_oob() may
  	 * return -EUCLEAN. In all cases, perform similar logic to mtd_read().
  	 */
  	ret_code = mtd->_read_oob(mtd, from, ops);
  	if (unlikely(ret_code < 0))
  		return ret_code;
  	if (mtd->ecc_strength == 0)
  		return 0;	/* device lacks ecc */
  	return ret_code >= mtd->bitflip_threshold ? -EUCLEAN : 0;
d2d48480d   Brian Norris   mtd: move mtd_rea...
1090
1091
  }
  EXPORT_SYMBOL_GPL(mtd_read_oob);
0c034fe37   Ezequiel Garcia   mtd: Uninline mtd...
1092
1093
1094
1095
1096
1097
1098
1099
  int mtd_write_oob(struct mtd_info *mtd, loff_t to,
  				struct mtd_oob_ops *ops)
  {
  	ops->retlen = ops->oobretlen = 0;
  	if (!mtd->_write_oob)
  		return -EOPNOTSUPP;
  	if (!(mtd->flags & MTD_WRITEABLE))
  		return -EROFS;
fea728c09   Ezequiel Garcia   mtd: Hook I/O act...
1100
  	ledtrig_mtd_activity();
0c034fe37   Ezequiel Garcia   mtd: Uninline mtd...
1101
1102
1103
  	return mtd->_write_oob(mtd, to, ops);
  }
  EXPORT_SYMBOL_GPL(mtd_write_oob);
75eb2cec2   Boris Brezillon   mtd: add mtd_oobl...
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
  /**
   * mtd_ooblayout_ecc - Get the OOB region definition of a specific ECC section
   * @mtd: MTD device structure
   * @section: ECC section. Depending on the layout you may have all the ECC
   *	     bytes stored in a single contiguous section, or one section
   *	     per ECC chunk (and sometime several sections for a single ECC
   *	     ECC chunk)
   * @oobecc: OOB region struct filled with the appropriate ECC position
   *	    information
   *
7da0fffb3   Masahiro Yamada   mtd: fix typos in...
1114
   * This function returns ECC section information in the OOB area. If you want
75eb2cec2   Boris Brezillon   mtd: add mtd_oobl...
1115
1116
1117
1118
1119
1120
1121
1122
   * to get all the ECC bytes information, then you should call
   * mtd_ooblayout_ecc(mtd, section++, oobecc) until it returns -ERANGE.
   *
   * Returns zero on success, a negative error code otherwise.
   */
  int mtd_ooblayout_ecc(struct mtd_info *mtd, int section,
  		      struct mtd_oob_region *oobecc)
  {
75eb2cec2   Boris Brezillon   mtd: add mtd_oobl...
1123
1124
1125
1126
  	memset(oobecc, 0, sizeof(*oobecc));
  
  	if (!mtd || section < 0)
  		return -EINVAL;
adbbc3bc8   Boris Brezillon   mtd: create an mt...
1127
  	if (!mtd->ooblayout || !mtd->ooblayout->ecc)
75eb2cec2   Boris Brezillon   mtd: add mtd_oobl...
1128
  		return -ENOTSUPP;
adbbc3bc8   Boris Brezillon   mtd: create an mt...
1129
  	return mtd->ooblayout->ecc(mtd, section, oobecc);
75eb2cec2   Boris Brezillon   mtd: add mtd_oobl...
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
  }
  EXPORT_SYMBOL_GPL(mtd_ooblayout_ecc);
  
  /**
   * mtd_ooblayout_free - Get the OOB region definition of a specific free
   *			section
   * @mtd: MTD device structure
   * @section: Free section you are interested in. Depending on the layout
   *	     you may have all the free bytes stored in a single contiguous
   *	     section, or one section per ECC chunk plus an extra section
   *	     for the remaining bytes (or other funky layout).
   * @oobfree: OOB region struct filled with the appropriate free position
   *	     information
   *
7da0fffb3   Masahiro Yamada   mtd: fix typos in...
1144
   * This function returns free bytes position in the OOB area. If you want
75eb2cec2   Boris Brezillon   mtd: add mtd_oobl...
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
   * to get all the free bytes information, then you should call
   * mtd_ooblayout_free(mtd, section++, oobfree) until it returns -ERANGE.
   *
   * Returns zero on success, a negative error code otherwise.
   */
  int mtd_ooblayout_free(struct mtd_info *mtd, int section,
  		       struct mtd_oob_region *oobfree)
  {
  	memset(oobfree, 0, sizeof(*oobfree));
  
  	if (!mtd || section < 0)
  		return -EINVAL;
adbbc3bc8   Boris Brezillon   mtd: create an mt...
1157
  	if (!mtd->ooblayout || !mtd->ooblayout->free)
75eb2cec2   Boris Brezillon   mtd: add mtd_oobl...
1158
  		return -ENOTSUPP;
adbbc3bc8   Boris Brezillon   mtd: create an mt...
1159
  	return mtd->ooblayout->free(mtd, section, oobfree);
75eb2cec2   Boris Brezillon   mtd: add mtd_oobl...
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
  }
  EXPORT_SYMBOL_GPL(mtd_ooblayout_free);
  
  /**
   * mtd_ooblayout_find_region - Find the region attached to a specific byte
   * @mtd: mtd info structure
   * @byte: the byte we are searching for
   * @sectionp: pointer where the section id will be stored
   * @oobregion: used to retrieve the ECC position
   * @iter: iterator function. Should be either mtd_ooblayout_free or
   *	  mtd_ooblayout_ecc depending on the region type you're searching for
   *
7da0fffb3   Masahiro Yamada   mtd: fix typos in...
1172
   * This function returns the section id and oobregion information of a
75eb2cec2   Boris Brezillon   mtd: add mtd_oobl...
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
   * specific byte. For example, say you want to know where the 4th ECC byte is
   * stored, you'll use:
   *
   * mtd_ooblayout_find_region(mtd, 3, &section, &oobregion, mtd_ooblayout_ecc);
   *
   * Returns zero on success, a negative error code otherwise.
   */
  static int mtd_ooblayout_find_region(struct mtd_info *mtd, int byte,
  				int *sectionp, struct mtd_oob_region *oobregion,
  				int (*iter)(struct mtd_info *,
  					    int section,
  					    struct mtd_oob_region *oobregion))
  {
  	int pos = 0, ret, section = 0;
  
  	memset(oobregion, 0, sizeof(*oobregion));
  
  	while (1) {
  		ret = iter(mtd, section, oobregion);
  		if (ret)
  			return ret;
  
  		if (pos + oobregion->length > byte)
  			break;
  
  		pos += oobregion->length;
  		section++;
  	}
  
  	/*
  	 * Adjust region info to make it start at the beginning at the
  	 * 'start' ECC byte.
  	 */
  	oobregion->offset += byte - pos;
  	oobregion->length -= byte - pos;
  	*sectionp = section;
  
  	return 0;
  }
  
  /**
   * mtd_ooblayout_find_eccregion - Find the ECC region attached to a specific
   *				  ECC byte
   * @mtd: mtd info structure
   * @eccbyte: the byte we are searching for
   * @sectionp: pointer where the section id will be stored
   * @oobregion: OOB region information
   *
   * Works like mtd_ooblayout_find_region() except it searches for a specific ECC
   * byte.
   *
   * Returns zero on success, a negative error code otherwise.
   */
  int mtd_ooblayout_find_eccregion(struct mtd_info *mtd, int eccbyte,
  				 int *section,
  				 struct mtd_oob_region *oobregion)
  {
  	return mtd_ooblayout_find_region(mtd, eccbyte, section, oobregion,
  					 mtd_ooblayout_ecc);
  }
  EXPORT_SYMBOL_GPL(mtd_ooblayout_find_eccregion);
  
  /**
   * mtd_ooblayout_get_bytes - Extract OOB bytes from the oob buffer
   * @mtd: mtd info structure
   * @buf: destination buffer to store OOB bytes
   * @oobbuf: OOB buffer
   * @start: first byte to retrieve
   * @nbytes: number of bytes to retrieve
   * @iter: section iterator
   *
   * Extract bytes attached to a specific category (ECC or free)
   * from the OOB buffer and copy them into buf.
   *
   * Returns zero on success, a negative error code otherwise.
   */
  static int mtd_ooblayout_get_bytes(struct mtd_info *mtd, u8 *buf,
  				const u8 *oobbuf, int start, int nbytes,
  				int (*iter)(struct mtd_info *,
  					    int section,
  					    struct mtd_oob_region *oobregion))
  {
8e8fd4d1e   Masahiro Yamada   mtd: remove unnee...
1255
1256
  	struct mtd_oob_region oobregion;
  	int section, ret;
75eb2cec2   Boris Brezillon   mtd: add mtd_oobl...
1257
1258
1259
1260
1261
1262
  
  	ret = mtd_ooblayout_find_region(mtd, start, &section,
  					&oobregion, iter);
  
  	while (!ret) {
  		int cnt;
7c295ef96   Masahiro Yamada   mtd: use min_t() ...
1263
  		cnt = min_t(int, nbytes, oobregion.length);
75eb2cec2   Boris Brezillon   mtd: add mtd_oobl...
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
  		memcpy(buf, oobbuf + oobregion.offset, cnt);
  		buf += cnt;
  		nbytes -= cnt;
  
  		if (!nbytes)
  			break;
  
  		ret = iter(mtd, ++section, &oobregion);
  	}
  
  	return ret;
  }
  
  /**
   * mtd_ooblayout_set_bytes - put OOB bytes into the oob buffer
   * @mtd: mtd info structure
   * @buf: source buffer to get OOB bytes from
   * @oobbuf: OOB buffer
   * @start: first OOB byte to set
   * @nbytes: number of OOB bytes to set
   * @iter: section iterator
   *
   * Fill the OOB buffer with data provided in buf. The category (ECC or free)
   * is selected by passing the appropriate iterator.
   *
   * Returns zero on success, a negative error code otherwise.
   */
  static int mtd_ooblayout_set_bytes(struct mtd_info *mtd, const u8 *buf,
  				u8 *oobbuf, int start, int nbytes,
  				int (*iter)(struct mtd_info *,
  					    int section,
  					    struct mtd_oob_region *oobregion))
  {
8e8fd4d1e   Masahiro Yamada   mtd: remove unnee...
1297
1298
  	struct mtd_oob_region oobregion;
  	int section, ret;
75eb2cec2   Boris Brezillon   mtd: add mtd_oobl...
1299
1300
1301
1302
1303
1304
  
  	ret = mtd_ooblayout_find_region(mtd, start, &section,
  					&oobregion, iter);
  
  	while (!ret) {
  		int cnt;
7c295ef96   Masahiro Yamada   mtd: use min_t() ...
1305
  		cnt = min_t(int, nbytes, oobregion.length);
75eb2cec2   Boris Brezillon   mtd: add mtd_oobl...
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
  		memcpy(oobbuf + oobregion.offset, buf, cnt);
  		buf += cnt;
  		nbytes -= cnt;
  
  		if (!nbytes)
  			break;
  
  		ret = iter(mtd, ++section, &oobregion);
  	}
  
  	return ret;
  }
  
  /**
   * mtd_ooblayout_count_bytes - count the number of bytes in a OOB category
   * @mtd: mtd info structure
   * @iter: category iterator
   *
   * Count the number of bytes in a given category.
   *
   * Returns a positive value on success, a negative error code otherwise.
   */
  static int mtd_ooblayout_count_bytes(struct mtd_info *mtd,
  				int (*iter)(struct mtd_info *,
  					    int section,
  					    struct mtd_oob_region *oobregion))
  {
4d6aecfb7   Masahiro Yamada   mtd: remove unnee...
1333
  	struct mtd_oob_region oobregion;
75eb2cec2   Boris Brezillon   mtd: add mtd_oobl...
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
  	int section = 0, ret, nbytes = 0;
  
  	while (1) {
  		ret = iter(mtd, section++, &oobregion);
  		if (ret) {
  			if (ret == -ERANGE)
  				ret = nbytes;
  			break;
  		}
  
  		nbytes += oobregion.length;
  	}
  
  	return ret;
  }
  
  /**
   * mtd_ooblayout_get_eccbytes - extract ECC bytes from the oob buffer
   * @mtd: mtd info structure
   * @eccbuf: destination buffer to store ECC bytes
   * @oobbuf: OOB buffer
   * @start: first ECC byte to retrieve
   * @nbytes: number of ECC bytes to retrieve
   *
   * Works like mtd_ooblayout_get_bytes(), except it acts on ECC bytes.
   *
   * Returns zero on success, a negative error code otherwise.
   */
  int mtd_ooblayout_get_eccbytes(struct mtd_info *mtd, u8 *eccbuf,
  			       const u8 *oobbuf, int start, int nbytes)
  {
  	return mtd_ooblayout_get_bytes(mtd, eccbuf, oobbuf, start, nbytes,
  				       mtd_ooblayout_ecc);
  }
  EXPORT_SYMBOL_GPL(mtd_ooblayout_get_eccbytes);
  
  /**
   * mtd_ooblayout_set_eccbytes - set ECC bytes into the oob buffer
   * @mtd: mtd info structure
   * @eccbuf: source buffer to get ECC bytes from
   * @oobbuf: OOB buffer
   * @start: first ECC byte to set
   * @nbytes: number of ECC bytes to set
   *
   * Works like mtd_ooblayout_set_bytes(), except it acts on ECC bytes.
   *
   * Returns zero on success, a negative error code otherwise.
   */
  int mtd_ooblayout_set_eccbytes(struct mtd_info *mtd, const u8 *eccbuf,
  			       u8 *oobbuf, int start, int nbytes)
  {
  	return mtd_ooblayout_set_bytes(mtd, eccbuf, oobbuf, start, nbytes,
  				       mtd_ooblayout_ecc);
  }
  EXPORT_SYMBOL_GPL(mtd_ooblayout_set_eccbytes);
  
  /**
   * mtd_ooblayout_get_databytes - extract data bytes from the oob buffer
   * @mtd: mtd info structure
   * @databuf: destination buffer to store ECC bytes
   * @oobbuf: OOB buffer
   * @start: first ECC byte to retrieve
   * @nbytes: number of ECC bytes to retrieve
   *
   * Works like mtd_ooblayout_get_bytes(), except it acts on free bytes.
   *
   * Returns zero on success, a negative error code otherwise.
   */
  int mtd_ooblayout_get_databytes(struct mtd_info *mtd, u8 *databuf,
  				const u8 *oobbuf, int start, int nbytes)
  {
  	return mtd_ooblayout_get_bytes(mtd, databuf, oobbuf, start, nbytes,
  				       mtd_ooblayout_free);
  }
  EXPORT_SYMBOL_GPL(mtd_ooblayout_get_databytes);
  
  /**
   * mtd_ooblayout_get_eccbytes - set data bytes into the oob buffer
   * @mtd: mtd info structure
   * @eccbuf: source buffer to get data bytes from
   * @oobbuf: OOB buffer
   * @start: first ECC byte to set
   * @nbytes: number of ECC bytes to set
   *
   * Works like mtd_ooblayout_get_bytes(), except it acts on free bytes.
   *
   * Returns zero on success, a negative error code otherwise.
   */
  int mtd_ooblayout_set_databytes(struct mtd_info *mtd, const u8 *databuf,
  				u8 *oobbuf, int start, int nbytes)
  {
  	return mtd_ooblayout_set_bytes(mtd, databuf, oobbuf, start, nbytes,
  				       mtd_ooblayout_free);
  }
  EXPORT_SYMBOL_GPL(mtd_ooblayout_set_databytes);
  
  /**
   * mtd_ooblayout_count_freebytes - count the number of free bytes in OOB
   * @mtd: mtd info structure
   *
   * Works like mtd_ooblayout_count_bytes(), except it count free bytes.
   *
   * Returns zero on success, a negative error code otherwise.
   */
  int mtd_ooblayout_count_freebytes(struct mtd_info *mtd)
  {
  	return mtd_ooblayout_count_bytes(mtd, mtd_ooblayout_free);
  }
  EXPORT_SYMBOL_GPL(mtd_ooblayout_count_freebytes);
  
  /**
   * mtd_ooblayout_count_freebytes - count the number of ECC bytes in OOB
   * @mtd: mtd info structure
   *
   * Works like mtd_ooblayout_count_bytes(), except it count ECC bytes.
   *
   * Returns zero on success, a negative error code otherwise.
   */
  int mtd_ooblayout_count_eccbytes(struct mtd_info *mtd)
  {
  	return mtd_ooblayout_count_bytes(mtd, mtd_ooblayout_ecc);
  }
  EXPORT_SYMBOL_GPL(mtd_ooblayout_count_eccbytes);
de3cac935   Artem Bityutskiy   mtd: check for ze...
1457
1458
1459
1460
1461
  /*
   * Method to access the protection register area, present in some flash
   * devices. The user data is one time programmable but the factory data is read
   * only.
   */
4b78fc42f   Christian Riesch   mtd: Add a retlen...
1462
1463
  int mtd_get_fact_prot_info(struct mtd_info *mtd, size_t len, size_t *retlen,
  			   struct otp_info *buf)
de3cac935   Artem Bityutskiy   mtd: check for ze...
1464
1465
1466
1467
1468
  {
  	if (!mtd->_get_fact_prot_info)
  		return -EOPNOTSUPP;
  	if (!len)
  		return 0;
4b78fc42f   Christian Riesch   mtd: Add a retlen...
1469
  	return mtd->_get_fact_prot_info(mtd, len, retlen, buf);
de3cac935   Artem Bityutskiy   mtd: check for ze...
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
  }
  EXPORT_SYMBOL_GPL(mtd_get_fact_prot_info);
  
  int mtd_read_fact_prot_reg(struct mtd_info *mtd, loff_t from, size_t len,
  			   size_t *retlen, u_char *buf)
  {
  	*retlen = 0;
  	if (!mtd->_read_fact_prot_reg)
  		return -EOPNOTSUPP;
  	if (!len)
  		return 0;
  	return mtd->_read_fact_prot_reg(mtd, from, len, retlen, buf);
  }
  EXPORT_SYMBOL_GPL(mtd_read_fact_prot_reg);
4b78fc42f   Christian Riesch   mtd: Add a retlen...
1484
1485
  int mtd_get_user_prot_info(struct mtd_info *mtd, size_t len, size_t *retlen,
  			   struct otp_info *buf)
de3cac935   Artem Bityutskiy   mtd: check for ze...
1486
1487
1488
1489
1490
  {
  	if (!mtd->_get_user_prot_info)
  		return -EOPNOTSUPP;
  	if (!len)
  		return 0;
4b78fc42f   Christian Riesch   mtd: Add a retlen...
1491
  	return mtd->_get_user_prot_info(mtd, len, retlen, buf);
de3cac935   Artem Bityutskiy   mtd: check for ze...
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
  }
  EXPORT_SYMBOL_GPL(mtd_get_user_prot_info);
  
  int mtd_read_user_prot_reg(struct mtd_info *mtd, loff_t from, size_t len,
  			   size_t *retlen, u_char *buf)
  {
  	*retlen = 0;
  	if (!mtd->_read_user_prot_reg)
  		return -EOPNOTSUPP;
  	if (!len)
  		return 0;
  	return mtd->_read_user_prot_reg(mtd, from, len, retlen, buf);
  }
  EXPORT_SYMBOL_GPL(mtd_read_user_prot_reg);
  
  int mtd_write_user_prot_reg(struct mtd_info *mtd, loff_t to, size_t len,
  			    size_t *retlen, u_char *buf)
  {
9a78bc83b   Christian Riesch   mtd: Fix the beha...
1510
  	int ret;
de3cac935   Artem Bityutskiy   mtd: check for ze...
1511
1512
1513
1514
1515
  	*retlen = 0;
  	if (!mtd->_write_user_prot_reg)
  		return -EOPNOTSUPP;
  	if (!len)
  		return 0;
9a78bc83b   Christian Riesch   mtd: Fix the beha...
1516
1517
1518
1519
1520
1521
1522
1523
1524
  	ret = mtd->_write_user_prot_reg(mtd, to, len, retlen, buf);
  	if (ret)
  		return ret;
  
  	/*
  	 * If no data could be written at all, we are out of memory and
  	 * must return -ENOSPC.
  	 */
  	return (*retlen) ? 0 : -ENOSPC;
de3cac935   Artem Bityutskiy   mtd: check for ze...
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
  }
  EXPORT_SYMBOL_GPL(mtd_write_user_prot_reg);
  
  int mtd_lock_user_prot_reg(struct mtd_info *mtd, loff_t from, size_t len)
  {
  	if (!mtd->_lock_user_prot_reg)
  		return -EOPNOTSUPP;
  	if (!len)
  		return 0;
  	return mtd->_lock_user_prot_reg(mtd, from, len);
  }
  EXPORT_SYMBOL_GPL(mtd_lock_user_prot_reg);
8273a0c91   Artem Bityutskiy   mtd: add offset a...
1537
1538
1539
1540
1541
  /* Chip-supported device locking */
  int mtd_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
  {
  	if (!mtd->_lock)
  		return -EOPNOTSUPP;
0c2b4e214   Brian Norris   mtd: correct uppe...
1542
  	if (ofs < 0 || ofs >= mtd->size || len > mtd->size - ofs)
8273a0c91   Artem Bityutskiy   mtd: add offset a...
1543
  		return -EINVAL;
bcb1d2387   Artem Bityutskiy   mtd: move zero le...
1544
1545
  	if (!len)
  		return 0;
8273a0c91   Artem Bityutskiy   mtd: add offset a...
1546
1547
1548
1549
1550
1551
1552
1553
  	return mtd->_lock(mtd, ofs, len);
  }
  EXPORT_SYMBOL_GPL(mtd_lock);
  
  int mtd_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
  {
  	if (!mtd->_unlock)
  		return -EOPNOTSUPP;
0c2b4e214   Brian Norris   mtd: correct uppe...
1554
  	if (ofs < 0 || ofs >= mtd->size || len > mtd->size - ofs)
8273a0c91   Artem Bityutskiy   mtd: add offset a...
1555
  		return -EINVAL;
bcb1d2387   Artem Bityutskiy   mtd: move zero le...
1556
1557
  	if (!len)
  		return 0;
8273a0c91   Artem Bityutskiy   mtd: add offset a...
1558
1559
1560
1561
1562
1563
1564
1565
  	return mtd->_unlock(mtd, ofs, len);
  }
  EXPORT_SYMBOL_GPL(mtd_unlock);
  
  int mtd_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len)
  {
  	if (!mtd->_is_locked)
  		return -EOPNOTSUPP;
0c2b4e214   Brian Norris   mtd: correct uppe...
1566
  	if (ofs < 0 || ofs >= mtd->size || len > mtd->size - ofs)
8273a0c91   Artem Bityutskiy   mtd: add offset a...
1567
  		return -EINVAL;
bcb1d2387   Artem Bityutskiy   mtd: move zero le...
1568
1569
  	if (!len)
  		return 0;
8273a0c91   Artem Bityutskiy   mtd: add offset a...
1570
1571
1572
  	return mtd->_is_locked(mtd, ofs, len);
  }
  EXPORT_SYMBOL_GPL(mtd_is_locked);
8471bb73b   Ezequiel Garcia   mtd: Introduce mt...
1573
  int mtd_block_isreserved(struct mtd_info *mtd, loff_t ofs)
8273a0c91   Artem Bityutskiy   mtd: add offset a...
1574
  {
0c2b4e214   Brian Norris   mtd: correct uppe...
1575
  	if (ofs < 0 || ofs >= mtd->size)
8471bb73b   Ezequiel Garcia   mtd: Introduce mt...
1576
1577
  		return -EINVAL;
  	if (!mtd->_block_isreserved)
8273a0c91   Artem Bityutskiy   mtd: add offset a...
1578
  		return 0;
8471bb73b   Ezequiel Garcia   mtd: Introduce mt...
1579
1580
1581
1582
1583
1584
  	return mtd->_block_isreserved(mtd, ofs);
  }
  EXPORT_SYMBOL_GPL(mtd_block_isreserved);
  
  int mtd_block_isbad(struct mtd_info *mtd, loff_t ofs)
  {
0c2b4e214   Brian Norris   mtd: correct uppe...
1585
  	if (ofs < 0 || ofs >= mtd->size)
8273a0c91   Artem Bityutskiy   mtd: add offset a...
1586
  		return -EINVAL;
8471bb73b   Ezequiel Garcia   mtd: Introduce mt...
1587
1588
  	if (!mtd->_block_isbad)
  		return 0;
8273a0c91   Artem Bityutskiy   mtd: add offset a...
1589
1590
1591
1592
1593
1594
1595
1596
  	return mtd->_block_isbad(mtd, ofs);
  }
  EXPORT_SYMBOL_GPL(mtd_block_isbad);
  
  int mtd_block_markbad(struct mtd_info *mtd, loff_t ofs)
  {
  	if (!mtd->_block_markbad)
  		return -EOPNOTSUPP;
0c2b4e214   Brian Norris   mtd: correct uppe...
1597
  	if (ofs < 0 || ofs >= mtd->size)
8273a0c91   Artem Bityutskiy   mtd: add offset a...
1598
  		return -EINVAL;
664addc24   Artem Bityutskiy   mtd: remove R/O c...
1599
1600
  	if (!(mtd->flags & MTD_WRITEABLE))
  		return -EROFS;
8273a0c91   Artem Bityutskiy   mtd: add offset a...
1601
1602
1603
1604
1605
  	return mtd->_block_markbad(mtd, ofs);
  }
  EXPORT_SYMBOL_GPL(mtd_block_markbad);
  
  /*
52b020317   Artem Bityutskiy   mtd: clean-up the...
1606
1607
1608
1609
1610
1611
1612
1613
1614
   * default_mtd_writev - the default writev method
   * @mtd: mtd device description object pointer
   * @vecs: the vectors to write
   * @count: count of vectors in @vecs
   * @to: the MTD device offset to write to
   * @retlen: on exit contains the count of bytes written to the MTD device.
   *
   * This function returns zero in case of success and a negative error code in
   * case of failure.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1615
   */
1dbebd325   Artem Bityutskiy   mtd: harmonize mt...
1616
1617
  static int default_mtd_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
1618
1619
1620
1621
  {
  	unsigned long i;
  	size_t totlen = 0, thislen;
  	int ret = 0;
52b020317   Artem Bityutskiy   mtd: clean-up the...
1622
1623
1624
1625
1626
1627
1628
1629
1630
  	for (i = 0; i < count; i++) {
  		if (!vecs[i].iov_len)
  			continue;
  		ret = mtd_write(mtd, to, vecs[i].iov_len, &thislen,
  				vecs[i].iov_base);
  		totlen += thislen;
  		if (ret || thislen != vecs[i].iov_len)
  			break;
  		to += vecs[i].iov_len;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1631
  	}
52b020317   Artem Bityutskiy   mtd: clean-up the...
1632
  	*retlen = totlen;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1633
1634
  	return ret;
  }
1dbebd325   Artem Bityutskiy   mtd: harmonize mt...
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
  
  /*
   * mtd_writev - the vector-based MTD write method
   * @mtd: mtd device description object pointer
   * @vecs: the vectors to write
   * @count: count of vectors in @vecs
   * @to: the MTD device offset to write to
   * @retlen: on exit contains the count of bytes written to the MTD device.
   *
   * This function returns zero in case of success and a negative error code in
   * case of failure.
   */
  int mtd_writev(struct mtd_info *mtd, const struct kvec *vecs,
  	       unsigned long count, loff_t to, size_t *retlen)
  {
  	*retlen = 0;
664addc24   Artem Bityutskiy   mtd: remove R/O c...
1651
1652
  	if (!(mtd->flags & MTD_WRITEABLE))
  		return -EROFS;
3c3c10bba   Artem Bityutskiy   mtd: add leading ...
1653
  	if (!mtd->_writev)
1dbebd325   Artem Bityutskiy   mtd: harmonize mt...
1654
  		return default_mtd_writev(mtd, vecs, count, to, retlen);
3c3c10bba   Artem Bityutskiy   mtd: add leading ...
1655
  	return mtd->_writev(mtd, vecs, count, to, retlen);
1dbebd325   Artem Bityutskiy   mtd: harmonize mt...
1656
1657
  }
  EXPORT_SYMBOL_GPL(mtd_writev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1658

33b53716b   Grant Erickson   mtd: create funct...
1659
1660
  /**
   * mtd_kmalloc_up_to - allocate a contiguous buffer up to the specified size
52b020317   Artem Bityutskiy   mtd: clean-up the...
1661
1662
   * @mtd: mtd device description object pointer
   * @size: a pointer to the ideal or maximum size of the allocation, points
33b53716b   Grant Erickson   mtd: create funct...
1663
1664
1665
1666
1667
1668
1669
   *        to the actual allocation size on success.
   *
   * This routine attempts to allocate a contiguous kernel buffer up to
   * the specified size, backing off the size of the request exponentially
   * until the request succeeds or until the allocation size falls below
   * the system page size. This attempts to make sure it does not adversely
   * impact system performance, so when allocating more than one page, we
caf491916   Linus Torvalds   Revert "revert "R...
1670
1671
   * ask the memory allocator to avoid re-trying, swapping, writing back
   * or performing I/O.
33b53716b   Grant Erickson   mtd: create funct...
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
   *
   * Note, this function also makes sure that the allocated buffer is aligned to
   * the MTD device's min. I/O unit, i.e. the "mtd->writesize" value.
   *
   * This is called, for example by mtd_{read,write} and jffs2_scan_medium,
   * to handle smaller (i.e. degraded) buffer allocations under low- or
   * fragmented-memory situations where such reduced allocations, from a
   * requested ideal, are allowed.
   *
   * Returns a pointer to the allocated buffer on success; otherwise, NULL.
   */
  void *mtd_kmalloc_up_to(const struct mtd_info *mtd, size_t *size)
  {
d0164adc8   Mel Gorman   mm, page_alloc: d...
1685
  	gfp_t flags = __GFP_NOWARN | __GFP_DIRECT_RECLAIM | __GFP_NORETRY;
33b53716b   Grant Erickson   mtd: create funct...
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
  	size_t min_alloc = max_t(size_t, mtd->writesize, PAGE_SIZE);
  	void *kbuf;
  
  	*size = min_t(size_t, *size, KMALLOC_MAX_SIZE);
  
  	while (*size > min_alloc) {
  		kbuf = kmalloc(*size, flags);
  		if (kbuf)
  			return kbuf;
  
  		*size >>= 1;
  		*size = ALIGN(*size, mtd->writesize);
  	}
  
  	/*
  	 * For the last resort allocation allow 'kmalloc()' to do all sorts of
  	 * things (write-back, dropping caches, etc) by using GFP_KERNEL.
  	 */
  	return kmalloc(*size, GFP_KERNEL);
  }
33b53716b   Grant Erickson   mtd: create funct...
1706
  EXPORT_SYMBOL_GPL(mtd_kmalloc_up_to);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1707

2d2dce0eb   Pavel Machek   [PATCH] Kill ifde...
1708
  #ifdef CONFIG_PROC_FS
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1709
  /*====================================================================*/
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1710
  /* Support for /proc/mtd */
447d9bd82   Alexey Dobriyan   mtd: convert to s...
1711
  static int mtd_proc_show(struct seq_file *m, void *v)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1712
  {
f1332ba2f   Ben Hutchings   mtd: Introduce an...
1713
  	struct mtd_info *mtd;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1714

447d9bd82   Alexey Dobriyan   mtd: convert to s...
1715
1716
  	seq_puts(m, "dev:    size   erasesize  name
  ");
48b192686   Ingo Molnar   [PATCH] sem2mutex...
1717
  	mutex_lock(&mtd_table_mutex);
f1332ba2f   Ben Hutchings   mtd: Introduce an...
1718
  	mtd_for_each_device(mtd) {
447d9bd82   Alexey Dobriyan   mtd: convert to s...
1719
1720
1721
1722
  		seq_printf(m, "mtd%d: %8.8llx %8.8x \"%s\"
  ",
  			   mtd->index, (unsigned long long)mtd->size,
  			   mtd->erasesize, mtd->name);
d5ca51292   Wanlong Gao   mtd:fix the bad f...
1723
  	}
48b192686   Ingo Molnar   [PATCH] sem2mutex...
1724
  	mutex_unlock(&mtd_table_mutex);
d5ca51292   Wanlong Gao   mtd:fix the bad f...
1725
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1726
  }
447d9bd82   Alexey Dobriyan   mtd: convert to s...
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
  static int mtd_proc_open(struct inode *inode, struct file *file)
  {
  	return single_open(file, mtd_proc_show, NULL);
  }
  
  static const struct file_operations mtd_proc_ops = {
  	.open		= mtd_proc_open,
  	.read		= seq_read,
  	.llseek		= seq_lseek,
  	.release	= single_release,
  };
45b090761   Kevin Cernekee   [MTD] sysfs suppo...
1738
  #endif /* CONFIG_PROC_FS */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1739
1740
  /*====================================================================*/
  /* Init code */
445caaa20   Steve Longerbeam   mtd: Allocate bdi...
1741
  static struct backing_dev_info * __init mtd_bdi_init(char *name)
0661b1ac5   Jens Axboe   mtd: ensure that ...
1742
  {
445caaa20   Steve Longerbeam   mtd: Allocate bdi...
1743
  	struct backing_dev_info *bdi;
0661b1ac5   Jens Axboe   mtd: ensure that ...
1744
  	int ret;
fa06052d6   Jan Kara   mtd: Convert to d...
1745
  	bdi = bdi_alloc(GFP_KERNEL);
445caaa20   Steve Longerbeam   mtd: Allocate bdi...
1746
1747
  	if (!bdi)
  		return ERR_PTR(-ENOMEM);
0661b1ac5   Jens Axboe   mtd: ensure that ...
1748

fa06052d6   Jan Kara   mtd: Convert to d...
1749
1750
1751
1752
1753
  	bdi->name = name;
  	/*
  	 * We put '-0' suffix to the name to get the same name format as we
  	 * used to get. Since this is called only once, we get a unique name. 
  	 */
7c4cc3002   Jan Kara   bdi: Drop 'parent...
1754
  	ret = bdi_register(bdi, "%.28s-0", name);
0661b1ac5   Jens Axboe   mtd: ensure that ...
1755
  	if (ret)
fa06052d6   Jan Kara   mtd: Convert to d...
1756
  		bdi_put(bdi);
0661b1ac5   Jens Axboe   mtd: ensure that ...
1757

445caaa20   Steve Longerbeam   mtd: Allocate bdi...
1758
  	return ret ? ERR_PTR(ret) : bdi;
0661b1ac5   Jens Axboe   mtd: ensure that ...
1759
  }
93e562141   Artem Bityutskiy   mtd: mtdcore: rem...
1760
  static struct proc_dir_entry *proc_mtd;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1761
1762
  static int __init init_mtd(void)
  {
15bce40cb   David Woodhouse   [MTD] Restore sus...
1763
  	int ret;
0661b1ac5   Jens Axboe   mtd: ensure that ...
1764

15bce40cb   David Woodhouse   [MTD] Restore sus...
1765
  	ret = class_register(&mtd_class);
0661b1ac5   Jens Axboe   mtd: ensure that ...
1766
1767
  	if (ret)
  		goto err_reg;
445caaa20   Steve Longerbeam   mtd: Allocate bdi...
1768
1769
1770
  	mtd_bdi = mtd_bdi_init("mtd");
  	if (IS_ERR(mtd_bdi)) {
  		ret = PTR_ERR(mtd_bdi);
b4caecd48   Christoph Hellwig   fs: introduce f_o...
1771
  		goto err_bdi;
445caaa20   Steve Longerbeam   mtd: Allocate bdi...
1772
  	}
694bb7fc1   Kevin Cernekee   [MTD] driver mode...
1773

447d9bd82   Alexey Dobriyan   mtd: convert to s...
1774
  	proc_mtd = proc_create("mtd", 0, NULL, &mtd_proc_ops);
93e562141   Artem Bityutskiy   mtd: mtdcore: rem...
1775

660685d9d   Artem Bityutskiy   mtd: merge mtdcha...
1776
1777
1778
  	ret = init_mtdchar();
  	if (ret)
  		goto out_procfs;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1779
  	return 0;
0661b1ac5   Jens Axboe   mtd: ensure that ...
1780

660685d9d   Artem Bityutskiy   mtd: merge mtdcha...
1781
1782
1783
  out_procfs:
  	if (proc_mtd)
  		remove_proc_entry("mtd", NULL);
fa06052d6   Jan Kara   mtd: Convert to d...
1784
  	bdi_put(mtd_bdi);
b4caecd48   Christoph Hellwig   fs: introduce f_o...
1785
  err_bdi:
0661b1ac5   Jens Axboe   mtd: ensure that ...
1786
1787
1788
1789
1790
  	class_unregister(&mtd_class);
  err_reg:
  	pr_err("Error registering mtd class or bdi: %d
  ", ret);
  	return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1791
1792
1793
1794
  }
  
  static void __exit cleanup_mtd(void)
  {
660685d9d   Artem Bityutskiy   mtd: merge mtdcha...
1795
  	cleanup_mtdchar();
d5ca51292   Wanlong Gao   mtd:fix the bad f...
1796
  	if (proc_mtd)
93e562141   Artem Bityutskiy   mtd: mtdcore: rem...
1797
  		remove_proc_entry("mtd", NULL);
15bce40cb   David Woodhouse   [MTD] Restore sus...
1798
  	class_unregister(&mtd_class);
fa06052d6   Jan Kara   mtd: Convert to d...
1799
  	bdi_put(mtd_bdi);
35667b998   Johannes Thumshirn   mtd: Destroy mtd_...
1800
  	idr_destroy(&mtd_idr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1801
1802
1803
1804
  }
  
  module_init(init_mtd);
  module_exit(cleanup_mtd);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1805
1806
1807
  MODULE_LICENSE("GPL");
  MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
  MODULE_DESCRIPTION("Core MTD registration and access routines");