mtd: nand: Add driver for Ricoh xD/SmartMedia reader
[GitHub/mt8127/android_kernel_alcatel_ttab.git] / drivers / mtd / mtd_blkdevs.c
CommitLineData
1da177e4 1/*
1da177e4
LT
2 * (C) 2003 David Woodhouse <dwmw2@infradead.org>
3 *
4 * Interface to Linux 2.5 block layer for MTD 'translation layers'.
5 *
6 */
7
8#include <linux/kernel.h>
9#include <linux/slab.h>
10#include <linux/module.h>
11#include <linux/list.h>
12#include <linux/fs.h>
13#include <linux/mtd/blktrans.h>
14#include <linux/mtd/mtd.h>
15#include <linux/blkdev.h>
16#include <linux/blkpg.h>
17#include <linux/spinlock.h>
18#include <linux/hdreg.h>
19#include <linux/init.h>
48b19268 20#include <linux/mutex.h>
99f9b243 21#include <linux/kthread.h>
1da177e4 22#include <asm/uaccess.h>
1da177e4 23
356d70f1 24#include "mtdcore.h"
1da177e4 25
356d70f1 26static LIST_HEAD(blktrans_majors);
048d8719
ML
27static DEFINE_MUTEX(blktrans_ref_mutex);
28
29void blktrans_dev_release(struct kref *kref)
30{
31 struct mtd_blktrans_dev *dev =
32 container_of(kref, struct mtd_blktrans_dev, ref);
33
34 dev->disk->private_data = NULL;
35 put_disk(dev->disk);
36 list_del(&dev->list);
37 kfree(dev);
38}
39
40static struct mtd_blktrans_dev *blktrans_dev_get(struct gendisk *disk)
41{
42 struct mtd_blktrans_dev *dev;
43
44 mutex_lock(&blktrans_ref_mutex);
45 dev = disk->private_data;
46
47 if (!dev)
48 goto unlock;
49 kref_get(&dev->ref);
50unlock:
51 mutex_unlock(&blktrans_ref_mutex);
52 return dev;
53}
54
55void blktrans_dev_put(struct mtd_blktrans_dev *dev)
56{
57 mutex_lock(&blktrans_ref_mutex);
58 kref_put(&dev->ref, blktrans_dev_release);
59 mutex_unlock(&blktrans_ref_mutex);
60}
1da177e4 61
1da177e4
LT
62
63static int do_blktrans_request(struct mtd_blktrans_ops *tr,
64 struct mtd_blktrans_dev *dev,
65 struct request *req)
66{
67 unsigned long block, nsect;
68 char *buf;
69
83096ebf 70 block = blk_rq_pos(req) << 9 >> tr->blkshift;
1011c1b9 71 nsect = blk_rq_cur_bytes(req) >> tr->blkshift;
19187672 72
1da177e4
LT
73 buf = req->buffer;
74
4aff5e23 75 if (!blk_fs_request(req))
f06d9a2b 76 return -EIO;
1da177e4 77
83096ebf
TH
78 if (blk_rq_pos(req) + blk_rq_cur_sectors(req) >
79 get_capacity(req->rq_disk))
f06d9a2b 80 return -EIO;
1da177e4 81
1122a26f
CH
82 if (blk_discard_rq(req))
83 return tr->discard(dev, block, nsect);
84
1da177e4
LT
85 switch(rq_data_dir(req)) {
86 case READ:
19187672 87 for (; nsect > 0; nsect--, block++, buf += tr->blksize)
1da177e4 88 if (tr->readsect(dev, block, buf))
f06d9a2b 89 return -EIO;
2d4dc890 90 rq_flush_dcache_pages(req);
f06d9a2b 91 return 0;
1da177e4
LT
92 case WRITE:
93 if (!tr->writesect)
f06d9a2b 94 return -EIO;
1da177e4 95
2d4dc890 96 rq_flush_dcache_pages(req);
19187672 97 for (; nsect > 0; nsect--, block++, buf += tr->blksize)
1da177e4 98 if (tr->writesect(dev, block, buf))
f06d9a2b
TH
99 return -EIO;
100 return 0;
1da177e4 101 default:
9a292308 102 printk(KERN_NOTICE "Unknown request %u\n", rq_data_dir(req));
f06d9a2b 103 return -EIO;
1da177e4
LT
104 }
105}
106
107static int mtd_blktrans_thread(void *arg)
108{
a8638622
ML
109 struct mtd_blktrans_dev *dev = arg;
110 struct request_queue *rq = dev->rq;
1498ada7 111 struct request *req = NULL;
1da177e4 112
1da177e4 113 spin_lock_irq(rq->queue_lock);
1498ada7 114
3e67fe45 115 while (!kthread_should_stop()) {
f06d9a2b 116 int res;
1da177e4 117
9934c8c0 118 if (!req && !(req = blk_fetch_request(rq))) {
1da177e4 119 set_current_state(TASK_INTERRUPTIBLE);
1da177e4 120 spin_unlock_irq(rq->queue_lock);
1da177e4 121 schedule();
1da177e4 122 spin_lock_irq(rq->queue_lock);
1da177e4
LT
123 continue;
124 }
125
1da177e4
LT
126 spin_unlock_irq(rq->queue_lock);
127
48b19268 128 mutex_lock(&dev->lock);
a8638622 129 res = do_blktrans_request(dev->tr, dev, req);
48b19268 130 mutex_unlock(&dev->lock);
1da177e4
LT
131
132 spin_lock_irq(rq->queue_lock);
133
1498ada7
TH
134 if (!__blk_end_request_cur(req, res))
135 req = NULL;
1da177e4 136 }
1498ada7
TH
137
138 if (req)
139 __blk_end_request_all(req, -EIO);
140
1da177e4
LT
141 spin_unlock_irq(rq->queue_lock);
142
3e67fe45 143 return 0;
1da177e4
LT
144}
145
146static void mtd_blktrans_request(struct request_queue *rq)
147{
048d8719
ML
148 struct mtd_blktrans_dev *dev;
149 struct request *req = NULL;
150
151 dev = rq->queuedata;
1da177e4 152
048d8719
ML
153 if (!dev)
154 while ((req = blk_fetch_request(rq)) != NULL)
155 __blk_end_request_all(req, -ENODEV);
156 else
157 wake_up_process(dev->thread);
158}
1da177e4 159
af0e2a0a 160static int blktrans_open(struct block_device *bdev, fmode_t mode)
1da177e4 161{
048d8719
ML
162 struct mtd_blktrans_dev *dev = blktrans_dev_get(bdev->bd_disk);
163 int ret;
164
165 if (!dev)
166 return -ERESTARTSYS;
167
168 mutex_lock(&dev->lock);
169
170 if (!dev->mtd) {
171 ret = -ENXIO;
172 goto unlock;
1da177e4 173 }
048d8719
ML
174
175 ret = !dev->open++ && dev->tr->open ? dev->tr->open(dev) : 0;
176
177 /* Take another reference on the device so it won't go away till
178 last release */
179 if (!ret)
180 kref_get(&dev->ref);
181unlock:
182 mutex_unlock(&dev->lock);
183 blktrans_dev_put(dev);
1da177e4
LT
184 return ret;
185}
186
af0e2a0a 187static int blktrans_release(struct gendisk *disk, fmode_t mode)
1da177e4 188{
048d8719
ML
189 struct mtd_blktrans_dev *dev = blktrans_dev_get(disk);
190 int ret = -ENXIO;
1da177e4 191
048d8719
ML
192 if (!dev)
193 return ret;
1da177e4 194
048d8719
ML
195 mutex_lock(&dev->lock);
196
197 /* Release one reference, we sure its not the last one here*/
198 kref_put(&dev->ref, blktrans_dev_release);
1da177e4 199
048d8719
ML
200 if (!dev->mtd)
201 goto unlock;
202
203 ret = !--dev->open && dev->tr->release ? dev->tr->release(dev) : 0;
204unlock:
205 mutex_unlock(&dev->lock);
206 blktrans_dev_put(dev);
1da177e4
LT
207 return ret;
208}
209
a885c8c4
CH
210static int blktrans_getgeo(struct block_device *bdev, struct hd_geometry *geo)
211{
048d8719
ML
212 struct mtd_blktrans_dev *dev = blktrans_dev_get(bdev->bd_disk);
213 int ret = -ENXIO;
214
215 if (!dev)
216 return ret;
217
218 mutex_lock(&dev->lock);
a885c8c4 219
048d8719
ML
220 if (!dev->mtd)
221 goto unlock;
222
223 ret = dev->tr->getgeo ? dev->tr->getgeo(dev, geo) : 0;
224unlock:
225 mutex_unlock(&dev->lock);
226 blktrans_dev_put(dev);
227 return ret;
a885c8c4 228}
1da177e4 229
af0e2a0a 230static int blktrans_ioctl(struct block_device *bdev, fmode_t mode,
1da177e4
LT
231 unsigned int cmd, unsigned long arg)
232{
048d8719
ML
233 struct mtd_blktrans_dev *dev = blktrans_dev_get(bdev->bd_disk);
234 int ret = -ENXIO;
235
236 if (!dev)
237 return ret;
238
239 mutex_lock(&dev->lock);
240
241 if (!dev->mtd)
242 goto unlock;
1da177e4
LT
243
244 switch (cmd) {
245 case BLKFLSBUF:
048d8719 246 ret = dev->tr->flush ? dev->tr->flush(dev) : 0;
1da177e4 247 default:
048d8719 248 ret = -ENOTTY;
1da177e4 249 }
048d8719
ML
250unlock:
251 mutex_unlock(&dev->lock);
252 blktrans_dev_put(dev);
253 return ret;
1da177e4
LT
254}
255
83d5cde4 256static const struct block_device_operations mtd_blktrans_ops = {
1da177e4 257 .owner = THIS_MODULE,
af0e2a0a
AV
258 .open = blktrans_open,
259 .release = blktrans_release,
260 .locked_ioctl = blktrans_ioctl,
a885c8c4 261 .getgeo = blktrans_getgeo,
1da177e4
LT
262};
263
264int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new)
265{
266 struct mtd_blktrans_ops *tr = new->tr;
71a928c0 267 struct mtd_blktrans_dev *d;
1da177e4
LT
268 int last_devnum = -1;
269 struct gendisk *gd;
a8638622 270 int ret;
1da177e4 271
b3561ea9 272 if (mutex_trylock(&mtd_table_mutex)) {
48b19268 273 mutex_unlock(&mtd_table_mutex);
1da177e4
LT
274 BUG();
275 }
276
048d8719 277 mutex_lock(&blktrans_ref_mutex);
71a928c0 278 list_for_each_entry(d, &tr->devs, list) {
1da177e4
LT
279 if (new->devnum == -1) {
280 /* Use first free number */
281 if (d->devnum != last_devnum+1) {
282 /* Found a free devnum. Plug it in here */
283 new->devnum = last_devnum+1;
284 list_add_tail(&new->list, &d->list);
285 goto added;
286 }
287 } else if (d->devnum == new->devnum) {
288 /* Required number taken */
048d8719 289 mutex_unlock(&blktrans_ref_mutex);
1da177e4
LT
290 return -EBUSY;
291 } else if (d->devnum > new->devnum) {
292 /* Required number was free */
293 list_add_tail(&new->list, &d->list);
294 goto added;
97894cda 295 }
1da177e4
LT
296 last_devnum = d->devnum;
297 }
a8638622
ML
298
299 ret = -EBUSY;
1da177e4
LT
300 if (new->devnum == -1)
301 new->devnum = last_devnum+1;
302
4d3a8534
BH
303 /* Check that the device and any partitions will get valid
304 * minor numbers and that the disk naming code below can cope
305 * with this number. */
306 if (new->devnum > (MINORMASK >> tr->part_bits) ||
048d8719
ML
307 (tr->part_bits && new->devnum >= 27 * 26)) {
308 mutex_unlock(&blktrans_ref_mutex);
a8638622 309 goto error1;
048d8719 310 }
1da177e4 311
1da177e4
LT
312 list_add_tail(&new->list, &tr->devs);
313 added:
048d8719
ML
314 mutex_unlock(&blktrans_ref_mutex);
315
ce37ab42 316 mutex_init(&new->lock);
048d8719 317 kref_init(&new->ref);
1da177e4
LT
318 if (!tr->writesect)
319 new->readonly = 1;
320
a8638622
ML
321 /* Create gendisk */
322 ret = -ENOMEM;
1da177e4 323 gd = alloc_disk(1 << tr->part_bits);
a8638622
ML
324
325 if (!gd)
326 goto error2;
327
328 new->disk = gd;
329 gd->private_data = new;
1da177e4
LT
330 gd->major = tr->major;
331 gd->first_minor = (new->devnum) << tr->part_bits;
332 gd->fops = &mtd_blktrans_ops;
97894cda 333
65a8de36
TP
334 if (tr->part_bits)
335 if (new->devnum < 26)
336 snprintf(gd->disk_name, sizeof(gd->disk_name),
337 "%s%c", tr->name, 'a' + new->devnum);
338 else
339 snprintf(gd->disk_name, sizeof(gd->disk_name),
340 "%s%c%c", tr->name,
341 'a' - 1 + new->devnum / 26,
342 'a' + new->devnum % 26);
343 else
344 snprintf(gd->disk_name, sizeof(gd->disk_name),
345 "%s%d", tr->name, new->devnum);
1da177e4 346
19187672 347 set_capacity(gd, (new->size * tr->blksize) >> 9);
1da177e4 348
a8638622
ML
349 /* Create the request queue */
350 spin_lock_init(&new->queue_lock);
351 new->rq = blk_init_queue(mtd_blktrans_request, &new->queue_lock);
352
353 if (!new->rq)
354 goto error3;
355
356 new->rq->queuedata = new;
357 blk_queue_logical_block_size(new->rq, tr->blksize);
358
359 if (tr->discard)
360 queue_flag_set_unlocked(QUEUE_FLAG_DISCARD,
361 new->rq);
362
363 gd->queue = new->rq;
364
048d8719
ML
365 __get_mtd_device(new->mtd);
366 __module_get(tr->owner);
367
a8638622
ML
368 /* Create processing thread */
369 /* TODO: workqueue ? */
370 new->thread = kthread_run(mtd_blktrans_thread, new,
371 "%s%d", tr->name, new->mtd->index);
372 if (IS_ERR(new->thread)) {
373 ret = PTR_ERR(new->thread);
374 goto error4;
375 }
d694846b 376 gd->driverfs_dev = &new->mtd->dev;
1da177e4
LT
377
378 if (new->readonly)
379 set_disk_ro(gd, 1);
380
381 add_disk(gd);
026ec578
ML
382
383 if (new->disk_attributes)
384 sysfs_create_group(&disk_to_dev(gd)->kobj,
385 new->disk_attributes);
1da177e4 386 return 0;
a8638622 387error4:
048d8719
ML
388 module_put(tr->owner);
389 __put_mtd_device(new->mtd);
a8638622
ML
390 blk_cleanup_queue(new->rq);
391error3:
392 put_disk(new->disk);
393error2:
394 list_del(&new->list);
395error1:
396 kfree(new);
397 return ret;
1da177e4
LT
398}
399
400int del_mtd_blktrans_dev(struct mtd_blktrans_dev *old)
401{
048d8719
ML
402 unsigned long flags;
403
b3561ea9 404 if (mutex_trylock(&mtd_table_mutex)) {
48b19268 405 mutex_unlock(&mtd_table_mutex);
1da177e4
LT
406 BUG();
407 }
408
048d8719 409 /* Stop new requests to arrive */
a8638622 410 del_gendisk(old->disk);
97894cda 411
026ec578
ML
412 if (old->disk_attributes)
413 sysfs_remove_group(&disk_to_dev(old->disk)->kobj,
414 old->disk_attributes);
415
a8638622
ML
416 /* Stop the thread */
417 kthread_stop(old->thread);
418
048d8719
ML
419 /* Kill current requests */
420 spin_lock_irqsave(&old->queue_lock, flags);
421 old->rq->queuedata = NULL;
422 blk_start_queue(old->rq);
423 spin_unlock_irqrestore(&old->queue_lock, flags);
a8638622 424 blk_cleanup_queue(old->rq);
048d8719
ML
425
426 /* Ask trans driver for release to the mtd device */
427 mutex_lock(&old->lock);
428 if (old->open && old->tr->release) {
429 old->tr->release(old);
430 old->open = 0;
431 }
432
433 __put_mtd_device(old->mtd);
434 module_put(old->tr->owner);
435
436 /* At that point, we don't touch the mtd anymore */
437 old->mtd = NULL;
438
439 mutex_unlock(&old->lock);
440 blktrans_dev_put(old);
1da177e4
LT
441 return 0;
442}
443
444static void blktrans_notify_remove(struct mtd_info *mtd)
445{
71a928c0
CM
446 struct mtd_blktrans_ops *tr;
447 struct mtd_blktrans_dev *dev, *next;
1da177e4 448
71a928c0
CM
449 list_for_each_entry(tr, &blktrans_majors, list)
450 list_for_each_entry_safe(dev, next, &tr->devs, list)
1da177e4
LT
451 if (dev->mtd == mtd)
452 tr->remove_dev(dev);
1da177e4
LT
453}
454
455static void blktrans_notify_add(struct mtd_info *mtd)
456{
71a928c0 457 struct mtd_blktrans_ops *tr;
1da177e4
LT
458
459 if (mtd->type == MTD_ABSENT)
460 return;
461
71a928c0 462 list_for_each_entry(tr, &blktrans_majors, list)
1da177e4 463 tr->add_mtd(tr, mtd);
1da177e4
LT
464}
465
466static struct mtd_notifier blktrans_notifier = {
467 .add = blktrans_notify_add,
468 .remove = blktrans_notify_remove,
469};
97894cda 470
1da177e4
LT
471int register_mtd_blktrans(struct mtd_blktrans_ops *tr)
472{
f1332ba2
BH
473 struct mtd_info *mtd;
474 int ret;
1da177e4 475
97894cda 476 /* Register the notifier if/when the first device type is
1da177e4
LT
477 registered, to prevent the link/init ordering from fucking
478 us over. */
479 if (!blktrans_notifier.list.next)
480 register_mtd_user(&blktrans_notifier);
481
1da177e4 482
48b19268 483 mutex_lock(&mtd_table_mutex);
1da177e4
LT
484
485 ret = register_blkdev(tr->major, tr->name);
486 if (ret) {
487 printk(KERN_WARNING "Unable to register %s block device on major %d: %d\n",
488 tr->name, tr->major, ret);
48b19268 489 mutex_unlock(&mtd_table_mutex);
1da177e4
LT
490 return ret;
491 }
eae9acd1 492
19187672 493 tr->blkshift = ffs(tr->blksize) - 1;
1da177e4 494
1da177e4
LT
495 INIT_LIST_HEAD(&tr->devs);
496 list_add(&tr->list, &blktrans_majors);
497
f1332ba2
BH
498 mtd_for_each_device(mtd)
499 if (mtd->type != MTD_ABSENT)
500 tr->add_mtd(tr, mtd);
1da177e4 501
48b19268 502 mutex_unlock(&mtd_table_mutex);
1da177e4
LT
503 return 0;
504}
505
506int deregister_mtd_blktrans(struct mtd_blktrans_ops *tr)
507{
71a928c0 508 struct mtd_blktrans_dev *dev, *next;
1da177e4 509
48b19268 510 mutex_lock(&mtd_table_mutex);
1da177e4 511
1da177e4
LT
512 /* Remove it from the list of active majors */
513 list_del(&tr->list);
514
71a928c0 515 list_for_each_entry_safe(dev, next, &tr->devs, list)
1da177e4 516 tr->remove_dev(dev);
1da177e4 517
1da177e4 518 unregister_blkdev(tr->major, tr->name);
48b19268 519 mutex_unlock(&mtd_table_mutex);
1da177e4 520
373ebfbf 521 BUG_ON(!list_empty(&tr->devs));
1da177e4
LT
522 return 0;
523}
524
525static void __exit mtd_blktrans_exit(void)
526{
527 /* No race here -- if someone's currently in register_mtd_blktrans
528 we're screwed anyway. */
529 if (blktrans_notifier.list.next)
530 unregister_mtd_user(&blktrans_notifier);
531}
532
533module_exit(mtd_blktrans_exit);
534
535EXPORT_SYMBOL_GPL(register_mtd_blktrans);
536EXPORT_SYMBOL_GPL(deregister_mtd_blktrans);
537EXPORT_SYMBOL_GPL(add_mtd_blktrans_dev);
538EXPORT_SYMBOL_GPL(del_mtd_blktrans_dev);
539
540MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
541MODULE_LICENSE("GPL");
542MODULE_DESCRIPTION("Common interface to block layer for MTD 'translation layers'");