diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/mmc/mmc_block.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/mmc/mmc_block.c')
-rw-r--r-- | drivers/mmc/mmc_block.c | 509 |
1 files changed, 509 insertions, 0 deletions
diff --git a/drivers/mmc/mmc_block.c b/drivers/mmc/mmc_block.c new file mode 100644 index 000000000000..b5b4a7b11903 --- /dev/null +++ b/drivers/mmc/mmc_block.c | |||
@@ -0,0 +1,509 @@ | |||
1 | /* | ||
2 | * Block driver for media (i.e., flash cards) | ||
3 | * | ||
4 | * Copyright 2002 Hewlett-Packard Company | ||
5 | * | ||
6 | * Use consistent with the GNU GPL is permitted, | ||
7 | * provided that this copyright notice is | ||
8 | * preserved in its entirety in all copies and derived works. | ||
9 | * | ||
10 | * HEWLETT-PACKARD COMPANY MAKES NO WARRANTIES, EXPRESSED OR IMPLIED, | ||
11 | * AS TO THE USEFULNESS OR CORRECTNESS OF THIS CODE OR ITS | ||
12 | * FITNESS FOR ANY PARTICULAR PURPOSE. | ||
13 | * | ||
14 | * Many thanks to Alessandro Rubini and Jonathan Corbet! | ||
15 | * | ||
16 | * Author: Andrew Christian | ||
17 | * 28 May 2002 | ||
18 | */ | ||
19 | #include <linux/moduleparam.h> | ||
20 | #include <linux/module.h> | ||
21 | #include <linux/init.h> | ||
22 | |||
23 | #include <linux/sched.h> | ||
24 | #include <linux/kernel.h> | ||
25 | #include <linux/fs.h> | ||
26 | #include <linux/errno.h> | ||
27 | #include <linux/hdreg.h> | ||
28 | #include <linux/kdev_t.h> | ||
29 | #include <linux/blkdev.h> | ||
30 | #include <linux/devfs_fs_kernel.h> | ||
31 | |||
32 | #include <linux/mmc/card.h> | ||
33 | #include <linux/mmc/protocol.h> | ||
34 | |||
35 | #include <asm/system.h> | ||
36 | #include <asm/uaccess.h> | ||
37 | |||
38 | #include "mmc_queue.h" | ||
39 | |||
40 | /* | ||
41 | * max 8 partitions per card | ||
42 | */ | ||
43 | #define MMC_SHIFT 3 | ||
44 | |||
45 | static int major; | ||
46 | |||
47 | /* | ||
48 | * There is one mmc_blk_data per slot. | ||
49 | */ | ||
50 | struct mmc_blk_data { | ||
51 | spinlock_t lock; | ||
52 | struct gendisk *disk; | ||
53 | struct mmc_queue queue; | ||
54 | |||
55 | unsigned int usage; | ||
56 | unsigned int block_bits; | ||
57 | }; | ||
58 | |||
59 | static DECLARE_MUTEX(open_lock); | ||
60 | |||
61 | static struct mmc_blk_data *mmc_blk_get(struct gendisk *disk) | ||
62 | { | ||
63 | struct mmc_blk_data *md; | ||
64 | |||
65 | down(&open_lock); | ||
66 | md = disk->private_data; | ||
67 | if (md && md->usage == 0) | ||
68 | md = NULL; | ||
69 | if (md) | ||
70 | md->usage++; | ||
71 | up(&open_lock); | ||
72 | |||
73 | return md; | ||
74 | } | ||
75 | |||
76 | static void mmc_blk_put(struct mmc_blk_data *md) | ||
77 | { | ||
78 | down(&open_lock); | ||
79 | md->usage--; | ||
80 | if (md->usage == 0) { | ||
81 | put_disk(md->disk); | ||
82 | mmc_cleanup_queue(&md->queue); | ||
83 | kfree(md); | ||
84 | } | ||
85 | up(&open_lock); | ||
86 | } | ||
87 | |||
88 | static int mmc_blk_open(struct inode *inode, struct file *filp) | ||
89 | { | ||
90 | struct mmc_blk_data *md; | ||
91 | int ret = -ENXIO; | ||
92 | |||
93 | md = mmc_blk_get(inode->i_bdev->bd_disk); | ||
94 | if (md) { | ||
95 | if (md->usage == 2) | ||
96 | check_disk_change(inode->i_bdev); | ||
97 | ret = 0; | ||
98 | } | ||
99 | |||
100 | return ret; | ||
101 | } | ||
102 | |||
103 | static int mmc_blk_release(struct inode *inode, struct file *filp) | ||
104 | { | ||
105 | struct mmc_blk_data *md = inode->i_bdev->bd_disk->private_data; | ||
106 | |||
107 | mmc_blk_put(md); | ||
108 | return 0; | ||
109 | } | ||
110 | |||
111 | static int | ||
112 | mmc_blk_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) | ||
113 | { | ||
114 | struct block_device *bdev = inode->i_bdev; | ||
115 | |||
116 | if (cmd == HDIO_GETGEO) { | ||
117 | struct hd_geometry geo; | ||
118 | |||
119 | memset(&geo, 0, sizeof(struct hd_geometry)); | ||
120 | |||
121 | geo.cylinders = get_capacity(bdev->bd_disk) / (4 * 16); | ||
122 | geo.heads = 4; | ||
123 | geo.sectors = 16; | ||
124 | geo.start = get_start_sect(bdev); | ||
125 | |||
126 | return copy_to_user((void __user *)arg, &geo, sizeof(geo)) | ||
127 | ? -EFAULT : 0; | ||
128 | } | ||
129 | |||
130 | return -ENOTTY; | ||
131 | } | ||
132 | |||
133 | static struct block_device_operations mmc_bdops = { | ||
134 | .open = mmc_blk_open, | ||
135 | .release = mmc_blk_release, | ||
136 | .ioctl = mmc_blk_ioctl, | ||
137 | .owner = THIS_MODULE, | ||
138 | }; | ||
139 | |||
140 | struct mmc_blk_request { | ||
141 | struct mmc_request mrq; | ||
142 | struct mmc_command cmd; | ||
143 | struct mmc_command stop; | ||
144 | struct mmc_data data; | ||
145 | }; | ||
146 | |||
147 | static int mmc_blk_prep_rq(struct mmc_queue *mq, struct request *req) | ||
148 | { | ||
149 | struct mmc_blk_data *md = mq->data; | ||
150 | int stat = BLKPREP_OK; | ||
151 | |||
152 | /* | ||
153 | * If we have no device, we haven't finished initialising. | ||
154 | */ | ||
155 | if (!md || !mq->card) { | ||
156 | printk(KERN_ERR "%s: killing request - no device/host\n", | ||
157 | req->rq_disk->disk_name); | ||
158 | stat = BLKPREP_KILL; | ||
159 | } | ||
160 | |||
161 | return stat; | ||
162 | } | ||
163 | |||
164 | static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) | ||
165 | { | ||
166 | struct mmc_blk_data *md = mq->data; | ||
167 | struct mmc_card *card = md->queue.card; | ||
168 | int ret; | ||
169 | |||
170 | if (mmc_card_claim_host(card)) | ||
171 | goto cmd_err; | ||
172 | |||
173 | do { | ||
174 | struct mmc_blk_request brq; | ||
175 | struct mmc_command cmd; | ||
176 | |||
177 | memset(&brq, 0, sizeof(struct mmc_blk_request)); | ||
178 | brq.mrq.cmd = &brq.cmd; | ||
179 | brq.mrq.data = &brq.data; | ||
180 | |||
181 | brq.cmd.arg = req->sector << 9; | ||
182 | brq.cmd.flags = MMC_RSP_R1; | ||
183 | brq.data.timeout_ns = card->csd.tacc_ns * 10; | ||
184 | brq.data.timeout_clks = card->csd.tacc_clks * 10; | ||
185 | brq.data.blksz_bits = md->block_bits; | ||
186 | brq.data.blocks = req->nr_sectors >> (md->block_bits - 9); | ||
187 | brq.stop.opcode = MMC_STOP_TRANSMISSION; | ||
188 | brq.stop.arg = 0; | ||
189 | brq.stop.flags = MMC_RSP_R1B; | ||
190 | |||
191 | if (rq_data_dir(req) == READ) { | ||
192 | brq.cmd.opcode = brq.data.blocks > 1 ? MMC_READ_MULTIPLE_BLOCK : MMC_READ_SINGLE_BLOCK; | ||
193 | brq.data.flags |= MMC_DATA_READ; | ||
194 | } else { | ||
195 | brq.cmd.opcode = MMC_WRITE_BLOCK; | ||
196 | brq.cmd.flags = MMC_RSP_R1B; | ||
197 | brq.data.flags |= MMC_DATA_WRITE; | ||
198 | brq.data.blocks = 1; | ||
199 | } | ||
200 | brq.mrq.stop = brq.data.blocks > 1 ? &brq.stop : NULL; | ||
201 | |||
202 | brq.data.sg = mq->sg; | ||
203 | brq.data.sg_len = blk_rq_map_sg(req->q, req, brq.data.sg); | ||
204 | |||
205 | mmc_wait_for_req(card->host, &brq.mrq); | ||
206 | if (brq.cmd.error) { | ||
207 | printk(KERN_ERR "%s: error %d sending read/write command\n", | ||
208 | req->rq_disk->disk_name, brq.cmd.error); | ||
209 | goto cmd_err; | ||
210 | } | ||
211 | |||
212 | if (brq.data.error) { | ||
213 | printk(KERN_ERR "%s: error %d transferring data\n", | ||
214 | req->rq_disk->disk_name, brq.data.error); | ||
215 | goto cmd_err; | ||
216 | } | ||
217 | |||
218 | if (brq.stop.error) { | ||
219 | printk(KERN_ERR "%s: error %d sending stop command\n", | ||
220 | req->rq_disk->disk_name, brq.stop.error); | ||
221 | goto cmd_err; | ||
222 | } | ||
223 | |||
224 | do { | ||
225 | int err; | ||
226 | |||
227 | cmd.opcode = MMC_SEND_STATUS; | ||
228 | cmd.arg = card->rca << 16; | ||
229 | cmd.flags = MMC_RSP_R1; | ||
230 | err = mmc_wait_for_cmd(card->host, &cmd, 5); | ||
231 | if (err) { | ||
232 | printk(KERN_ERR "%s: error %d requesting status\n", | ||
233 | req->rq_disk->disk_name, err); | ||
234 | goto cmd_err; | ||
235 | } | ||
236 | } while (!(cmd.resp[0] & R1_READY_FOR_DATA)); | ||
237 | |||
238 | #if 0 | ||
239 | if (cmd.resp[0] & ~0x00000900) | ||
240 | printk(KERN_ERR "%s: status = %08x\n", | ||
241 | req->rq_disk->disk_name, cmd.resp[0]); | ||
242 | if (mmc_decode_status(cmd.resp)) | ||
243 | goto cmd_err; | ||
244 | #endif | ||
245 | |||
246 | /* | ||
247 | * A block was successfully transferred. | ||
248 | */ | ||
249 | spin_lock_irq(&md->lock); | ||
250 | ret = end_that_request_chunk(req, 1, brq.data.bytes_xfered); | ||
251 | if (!ret) { | ||
252 | /* | ||
253 | * The whole request completed successfully. | ||
254 | */ | ||
255 | add_disk_randomness(req->rq_disk); | ||
256 | blkdev_dequeue_request(req); | ||
257 | end_that_request_last(req); | ||
258 | } | ||
259 | spin_unlock_irq(&md->lock); | ||
260 | } while (ret); | ||
261 | |||
262 | mmc_card_release_host(card); | ||
263 | |||
264 | return 1; | ||
265 | |||
266 | cmd_err: | ||
267 | mmc_card_release_host(card); | ||
268 | |||
269 | /* | ||
270 | * This is a little draconian, but until we get proper | ||
271 | * error handling sorted out here, its the best we can | ||
272 | * do - especially as some hosts have no idea how much | ||
273 | * data was transferred before the error occurred. | ||
274 | */ | ||
275 | spin_lock_irq(&md->lock); | ||
276 | do { | ||
277 | ret = end_that_request_chunk(req, 0, | ||
278 | req->current_nr_sectors << 9); | ||
279 | } while (ret); | ||
280 | |||
281 | add_disk_randomness(req->rq_disk); | ||
282 | blkdev_dequeue_request(req); | ||
283 | end_that_request_last(req); | ||
284 | spin_unlock_irq(&md->lock); | ||
285 | |||
286 | return 0; | ||
287 | } | ||
288 | |||
289 | #define MMC_NUM_MINORS (256 >> MMC_SHIFT) | ||
290 | |||
291 | static unsigned long dev_use[MMC_NUM_MINORS/(8*sizeof(unsigned long))]; | ||
292 | |||
293 | static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card) | ||
294 | { | ||
295 | struct mmc_blk_data *md; | ||
296 | int devidx, ret; | ||
297 | |||
298 | devidx = find_first_zero_bit(dev_use, MMC_NUM_MINORS); | ||
299 | if (devidx >= MMC_NUM_MINORS) | ||
300 | return ERR_PTR(-ENOSPC); | ||
301 | __set_bit(devidx, dev_use); | ||
302 | |||
303 | md = kmalloc(sizeof(struct mmc_blk_data), GFP_KERNEL); | ||
304 | if (md) { | ||
305 | memset(md, 0, sizeof(struct mmc_blk_data)); | ||
306 | |||
307 | md->disk = alloc_disk(1 << MMC_SHIFT); | ||
308 | if (md->disk == NULL) { | ||
309 | kfree(md); | ||
310 | md = ERR_PTR(-ENOMEM); | ||
311 | goto out; | ||
312 | } | ||
313 | |||
314 | spin_lock_init(&md->lock); | ||
315 | md->usage = 1; | ||
316 | |||
317 | ret = mmc_init_queue(&md->queue, card, &md->lock); | ||
318 | if (ret) { | ||
319 | put_disk(md->disk); | ||
320 | kfree(md); | ||
321 | md = ERR_PTR(ret); | ||
322 | goto out; | ||
323 | } | ||
324 | md->queue.prep_fn = mmc_blk_prep_rq; | ||
325 | md->queue.issue_fn = mmc_blk_issue_rq; | ||
326 | md->queue.data = md; | ||
327 | |||
328 | md->disk->major = major; | ||
329 | md->disk->first_minor = devidx << MMC_SHIFT; | ||
330 | md->disk->fops = &mmc_bdops; | ||
331 | md->disk->private_data = md; | ||
332 | md->disk->queue = md->queue.queue; | ||
333 | md->disk->driverfs_dev = &card->dev; | ||
334 | |||
335 | /* | ||
336 | * As discussed on lkml, GENHD_FL_REMOVABLE should: | ||
337 | * | ||
338 | * - be set for removable media with permanent block devices | ||
339 | * - be unset for removable block devices with permanent media | ||
340 | * | ||
341 | * Since MMC block devices clearly fall under the second | ||
342 | * case, we do not set GENHD_FL_REMOVABLE. Userspace | ||
343 | * should use the block device creation/destruction hotplug | ||
344 | * messages to tell when the card is present. | ||
345 | */ | ||
346 | |||
347 | sprintf(md->disk->disk_name, "mmcblk%d", devidx); | ||
348 | sprintf(md->disk->devfs_name, "mmc/blk%d", devidx); | ||
349 | |||
350 | md->block_bits = card->csd.read_blkbits; | ||
351 | |||
352 | blk_queue_hardsect_size(md->queue.queue, 1 << md->block_bits); | ||
353 | set_capacity(md->disk, card->csd.capacity); | ||
354 | } | ||
355 | out: | ||
356 | return md; | ||
357 | } | ||
358 | |||
359 | static int | ||
360 | mmc_blk_set_blksize(struct mmc_blk_data *md, struct mmc_card *card) | ||
361 | { | ||
362 | struct mmc_command cmd; | ||
363 | int err; | ||
364 | |||
365 | mmc_card_claim_host(card); | ||
366 | cmd.opcode = MMC_SET_BLOCKLEN; | ||
367 | cmd.arg = 1 << card->csd.read_blkbits; | ||
368 | cmd.flags = MMC_RSP_R1; | ||
369 | err = mmc_wait_for_cmd(card->host, &cmd, 5); | ||
370 | mmc_card_release_host(card); | ||
371 | |||
372 | if (err) { | ||
373 | printk(KERN_ERR "%s: unable to set block size to %d: %d\n", | ||
374 | md->disk->disk_name, cmd.arg, err); | ||
375 | return -EINVAL; | ||
376 | } | ||
377 | |||
378 | return 0; | ||
379 | } | ||
380 | |||
381 | static int mmc_blk_probe(struct mmc_card *card) | ||
382 | { | ||
383 | struct mmc_blk_data *md; | ||
384 | int err; | ||
385 | |||
386 | if (card->csd.cmdclass & ~0x1ff) | ||
387 | return -ENODEV; | ||
388 | |||
389 | if (card->csd.read_blkbits < 9) { | ||
390 | printk(KERN_WARNING "%s: read blocksize too small (%u)\n", | ||
391 | mmc_card_id(card), 1 << card->csd.read_blkbits); | ||
392 | return -ENODEV; | ||
393 | } | ||
394 | |||
395 | md = mmc_blk_alloc(card); | ||
396 | if (IS_ERR(md)) | ||
397 | return PTR_ERR(md); | ||
398 | |||
399 | err = mmc_blk_set_blksize(md, card); | ||
400 | if (err) | ||
401 | goto out; | ||
402 | |||
403 | printk(KERN_INFO "%s: %s %s %dKiB\n", | ||
404 | md->disk->disk_name, mmc_card_id(card), mmc_card_name(card), | ||
405 | (card->csd.capacity << card->csd.read_blkbits) / 1024); | ||
406 | |||
407 | mmc_set_drvdata(card, md); | ||
408 | add_disk(md->disk); | ||
409 | return 0; | ||
410 | |||
411 | out: | ||
412 | mmc_blk_put(md); | ||
413 | |||
414 | return err; | ||
415 | } | ||
416 | |||
417 | static void mmc_blk_remove(struct mmc_card *card) | ||
418 | { | ||
419 | struct mmc_blk_data *md = mmc_get_drvdata(card); | ||
420 | |||
421 | if (md) { | ||
422 | int devidx; | ||
423 | |||
424 | del_gendisk(md->disk); | ||
425 | |||
426 | /* | ||
427 | * I think this is needed. | ||
428 | */ | ||
429 | md->disk->queue = NULL; | ||
430 | |||
431 | devidx = md->disk->first_minor >> MMC_SHIFT; | ||
432 | __clear_bit(devidx, dev_use); | ||
433 | |||
434 | mmc_blk_put(md); | ||
435 | } | ||
436 | mmc_set_drvdata(card, NULL); | ||
437 | } | ||
438 | |||
439 | #ifdef CONFIG_PM | ||
440 | static int mmc_blk_suspend(struct mmc_card *card, pm_message_t state) | ||
441 | { | ||
442 | struct mmc_blk_data *md = mmc_get_drvdata(card); | ||
443 | |||
444 | if (md) { | ||
445 | mmc_queue_suspend(&md->queue); | ||
446 | } | ||
447 | return 0; | ||
448 | } | ||
449 | |||
450 | static int mmc_blk_resume(struct mmc_card *card) | ||
451 | { | ||
452 | struct mmc_blk_data *md = mmc_get_drvdata(card); | ||
453 | |||
454 | if (md) { | ||
455 | mmc_blk_set_blksize(md, card); | ||
456 | mmc_queue_resume(&md->queue); | ||
457 | } | ||
458 | return 0; | ||
459 | } | ||
460 | #else | ||
461 | #define mmc_blk_suspend NULL | ||
462 | #define mmc_blk_resume NULL | ||
463 | #endif | ||
464 | |||
465 | static struct mmc_driver mmc_driver = { | ||
466 | .drv = { | ||
467 | .name = "mmcblk", | ||
468 | }, | ||
469 | .probe = mmc_blk_probe, | ||
470 | .remove = mmc_blk_remove, | ||
471 | .suspend = mmc_blk_suspend, | ||
472 | .resume = mmc_blk_resume, | ||
473 | }; | ||
474 | |||
475 | static int __init mmc_blk_init(void) | ||
476 | { | ||
477 | int res = -ENOMEM; | ||
478 | |||
479 | res = register_blkdev(major, "mmc"); | ||
480 | if (res < 0) { | ||
481 | printk(KERN_WARNING "Unable to get major %d for MMC media: %d\n", | ||
482 | major, res); | ||
483 | goto out; | ||
484 | } | ||
485 | if (major == 0) | ||
486 | major = res; | ||
487 | |||
488 | devfs_mk_dir("mmc"); | ||
489 | return mmc_register_driver(&mmc_driver); | ||
490 | |||
491 | out: | ||
492 | return res; | ||
493 | } | ||
494 | |||
495 | static void __exit mmc_blk_exit(void) | ||
496 | { | ||
497 | mmc_unregister_driver(&mmc_driver); | ||
498 | devfs_remove("mmc"); | ||
499 | unregister_blkdev(major, "mmc"); | ||
500 | } | ||
501 | |||
502 | module_init(mmc_blk_init); | ||
503 | module_exit(mmc_blk_exit); | ||
504 | |||
505 | MODULE_LICENSE("GPL"); | ||
506 | MODULE_DESCRIPTION("Multimedia Card (MMC) block device driver"); | ||
507 | |||
508 | module_param(major, int, 0444); | ||
509 | MODULE_PARM_DESC(major, "specify the major device number for MMC block driver"); | ||