diff options
author | Russell King <rmk@dyn-67.arm.linux.org.uk> | 2006-01-03 17:38:44 -0500 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2006-01-03 17:38:44 -0500 |
commit | a6f6c96b65d7f65a7a7bf5cbe874eda182a6b2cc (patch) | |
tree | 5f3bd4dc24866f2b0e593b1457b1f22ec641139b /drivers/mmc | |
parent | 88026842b0a760145aa71d69e74fbc9ec118ca44 (diff) |
[MMC] Improve MMC card block size selection
Select a block size for IO based on the read and write block size
combinations, and whether the card supports partial block reads
and/or partial block writes.
If we are able to satisfy block reads but not block writes, mark
the device read only.
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'drivers/mmc')
-rw-r--r-- | drivers/mmc/mmc.c | 10 | ||||
-rw-r--r-- | drivers/mmc/mmc_block.c | 175 |
2 files changed, 123 insertions, 62 deletions
diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index eb41391e06e9..6696f71363b9 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c | |||
@@ -550,6 +550,11 @@ static void mmc_decode_csd(struct mmc_card *card) | |||
550 | csd->capacity = (1 + m) << (e + 2); | 550 | csd->capacity = (1 + m) << (e + 2); |
551 | 551 | ||
552 | csd->read_blkbits = UNSTUFF_BITS(resp, 80, 4); | 552 | csd->read_blkbits = UNSTUFF_BITS(resp, 80, 4); |
553 | csd->read_partial = UNSTUFF_BITS(resp, 79, 1); | ||
554 | csd->write_misalign = UNSTUFF_BITS(resp, 78, 1); | ||
555 | csd->read_misalign = UNSTUFF_BITS(resp, 77, 1); | ||
556 | csd->write_blkbits = UNSTUFF_BITS(resp, 22, 4); | ||
557 | csd->write_partial = UNSTUFF_BITS(resp, 21, 1); | ||
553 | } else { | 558 | } else { |
554 | /* | 559 | /* |
555 | * We only understand CSD structure v1.1 and v1.2. | 560 | * We only understand CSD structure v1.1 and v1.2. |
@@ -579,6 +584,11 @@ static void mmc_decode_csd(struct mmc_card *card) | |||
579 | csd->capacity = (1 + m) << (e + 2); | 584 | csd->capacity = (1 + m) << (e + 2); |
580 | 585 | ||
581 | csd->read_blkbits = UNSTUFF_BITS(resp, 80, 4); | 586 | csd->read_blkbits = UNSTUFF_BITS(resp, 80, 4); |
587 | csd->read_partial = UNSTUFF_BITS(resp, 79, 1); | ||
588 | csd->write_misalign = UNSTUFF_BITS(resp, 78, 1); | ||
589 | csd->read_misalign = UNSTUFF_BITS(resp, 77, 1); | ||
590 | csd->write_blkbits = UNSTUFF_BITS(resp, 22, 4); | ||
591 | csd->write_partial = UNSTUFF_BITS(resp, 21, 1); | ||
582 | } | 592 | } |
583 | } | 593 | } |
584 | 594 | ||
diff --git a/drivers/mmc/mmc_block.c b/drivers/mmc/mmc_block.c index abcf19116d70..b9837cc5b9ac 100644 --- a/drivers/mmc/mmc_block.c +++ b/drivers/mmc/mmc_block.c | |||
@@ -54,6 +54,7 @@ struct mmc_blk_data { | |||
54 | 54 | ||
55 | unsigned int usage; | 55 | unsigned int usage; |
56 | unsigned int block_bits; | 56 | unsigned int block_bits; |
57 | unsigned int read_only; | ||
57 | }; | 58 | }; |
58 | 59 | ||
59 | static DECLARE_MUTEX(open_lock); | 60 | static DECLARE_MUTEX(open_lock); |
@@ -85,12 +86,6 @@ static void mmc_blk_put(struct mmc_blk_data *md) | |||
85 | up(&open_lock); | 86 | up(&open_lock); |
86 | } | 87 | } |
87 | 88 | ||
88 | static inline int mmc_blk_readonly(struct mmc_card *card) | ||
89 | { | ||
90 | return mmc_card_readonly(card) || | ||
91 | !(card->csd.cmdclass & CCC_BLOCK_WRITE); | ||
92 | } | ||
93 | |||
94 | static int mmc_blk_open(struct inode *inode, struct file *filp) | 89 | static int mmc_blk_open(struct inode *inode, struct file *filp) |
95 | { | 90 | { |
96 | struct mmc_blk_data *md; | 91 | struct mmc_blk_data *md; |
@@ -102,8 +97,7 @@ static int mmc_blk_open(struct inode *inode, struct file *filp) | |||
102 | check_disk_change(inode->i_bdev); | 97 | check_disk_change(inode->i_bdev); |
103 | ret = 0; | 98 | ret = 0; |
104 | 99 | ||
105 | if ((filp->f_mode & FMODE_WRITE) && | 100 | if ((filp->f_mode & FMODE_WRITE) && md->read_only) |
106 | mmc_blk_readonly(md->queue.card)) | ||
107 | ret = -EROFS; | 101 | ret = -EROFS; |
108 | } | 102 | } |
109 | 103 | ||
@@ -299,6 +293,12 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) | |||
299 | 293 | ||
300 | static unsigned long dev_use[MMC_NUM_MINORS/(8*sizeof(unsigned long))]; | 294 | static unsigned long dev_use[MMC_NUM_MINORS/(8*sizeof(unsigned long))]; |
301 | 295 | ||
296 | static inline int mmc_blk_readonly(struct mmc_card *card) | ||
297 | { | ||
298 | return mmc_card_readonly(card) || | ||
299 | !(card->csd.cmdclass & CCC_BLOCK_WRITE); | ||
300 | } | ||
301 | |||
302 | static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card) | 302 | static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card) |
303 | { | 303 | { |
304 | struct mmc_blk_data *md; | 304 | struct mmc_blk_data *md; |
@@ -310,64 +310,121 @@ static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card) | |||
310 | __set_bit(devidx, dev_use); | 310 | __set_bit(devidx, dev_use); |
311 | 311 | ||
312 | md = kmalloc(sizeof(struct mmc_blk_data), GFP_KERNEL); | 312 | md = kmalloc(sizeof(struct mmc_blk_data), GFP_KERNEL); |
313 | if (md) { | 313 | if (!md) { |
314 | memset(md, 0, sizeof(struct mmc_blk_data)); | 314 | ret = -ENOMEM; |
315 | goto out; | ||
316 | } | ||
315 | 317 | ||
316 | md->disk = alloc_disk(1 << MMC_SHIFT); | 318 | memset(md, 0, sizeof(struct mmc_blk_data)); |
317 | if (md->disk == NULL) { | ||
318 | kfree(md); | ||
319 | md = ERR_PTR(-ENOMEM); | ||
320 | goto out; | ||
321 | } | ||
322 | 319 | ||
323 | spin_lock_init(&md->lock); | 320 | /* |
324 | md->usage = 1; | 321 | * Set the read-only status based on the supported commands |
322 | * and the write protect switch. | ||
323 | */ | ||
324 | md->read_only = mmc_blk_readonly(card); | ||
325 | 325 | ||
326 | ret = mmc_init_queue(&md->queue, card, &md->lock); | 326 | /* |
327 | if (ret) { | 327 | * Figure out a workable block size. MMC cards have: |
328 | put_disk(md->disk); | 328 | * - two block sizes, one for read and one for write. |
329 | kfree(md); | 329 | * - may support partial reads and/or writes |
330 | md = ERR_PTR(ret); | 330 | * (allows block sizes smaller than specified) |
331 | goto out; | 331 | */ |
332 | md->block_bits = card->csd.read_blkbits; | ||
333 | if (card->csd.write_blkbits != card->csd.read_blkbits) { | ||
334 | if (card->csd.write_blkbits < card->csd.read_blkbits && | ||
335 | card->csd.read_partial) { | ||
336 | /* | ||
337 | * write block size is smaller than read block | ||
338 | * size, but we support partial reads, so choose | ||
339 | * the smaller write block size. | ||
340 | */ | ||
341 | md->block_bits = card->csd.write_blkbits; | ||
342 | } else if (card->csd.write_blkbits > card->csd.read_blkbits && | ||
343 | card->csd.write_partial) { | ||
344 | /* | ||
345 | * read block size is smaller than write block | ||
346 | * size, but we support partial writes. Use read | ||
347 | * block size. | ||
348 | */ | ||
349 | } else { | ||
350 | /* | ||
351 | * We don't support this configuration for writes. | ||
352 | */ | ||
353 | printk(KERN_ERR "%s: unable to select block size for " | ||
354 | "writing (rb%u wb%u rp%u wp%u)\n", | ||
355 | md->disk->disk_name, | ||
356 | 1 << card->csd.read_blkbits, | ||
357 | 1 << card->csd.write_blkbits, | ||
358 | card->csd.read_partial, | ||
359 | card->csd.write_partial); | ||
360 | md->read_only = 1; | ||
332 | } | 361 | } |
333 | md->queue.prep_fn = mmc_blk_prep_rq; | 362 | } |
334 | md->queue.issue_fn = mmc_blk_issue_rq; | ||
335 | md->queue.data = md; | ||
336 | 363 | ||
337 | md->disk->major = major; | 364 | /* |
338 | md->disk->first_minor = devidx << MMC_SHIFT; | 365 | * Refuse to allow block sizes smaller than 512 bytes. |
339 | md->disk->fops = &mmc_bdops; | 366 | */ |
340 | md->disk->private_data = md; | 367 | if (md->block_bits < 9) { |
341 | md->disk->queue = md->queue.queue; | 368 | printk(KERN_ERR "%s: unable to support block size %u\n", |
342 | md->disk->driverfs_dev = &card->dev; | 369 | mmc_card_id(card), 1 << md->block_bits); |
370 | ret = -EINVAL; | ||
371 | goto err_kfree; | ||
372 | } | ||
343 | 373 | ||
344 | /* | 374 | md->disk = alloc_disk(1 << MMC_SHIFT); |
345 | * As discussed on lkml, GENHD_FL_REMOVABLE should: | 375 | if (md->disk == NULL) { |
346 | * | 376 | ret = -ENOMEM; |
347 | * - be set for removable media with permanent block devices | 377 | goto err_kfree; |
348 | * - be unset for removable block devices with permanent media | 378 | } |
349 | * | ||
350 | * Since MMC block devices clearly fall under the second | ||
351 | * case, we do not set GENHD_FL_REMOVABLE. Userspace | ||
352 | * should use the block device creation/destruction hotplug | ||
353 | * messages to tell when the card is present. | ||
354 | */ | ||
355 | 379 | ||
356 | sprintf(md->disk->disk_name, "mmcblk%d", devidx); | 380 | spin_lock_init(&md->lock); |
357 | sprintf(md->disk->devfs_name, "mmc/blk%d", devidx); | 381 | md->usage = 1; |
358 | 382 | ||
359 | md->block_bits = card->csd.read_blkbits; | 383 | ret = mmc_init_queue(&md->queue, card, &md->lock); |
384 | if (ret) | ||
385 | goto err_putdisk; | ||
360 | 386 | ||
361 | blk_queue_hardsect_size(md->queue.queue, 1 << md->block_bits); | 387 | md->queue.prep_fn = mmc_blk_prep_rq; |
388 | md->queue.issue_fn = mmc_blk_issue_rq; | ||
389 | md->queue.data = md; | ||
362 | 390 | ||
363 | /* | 391 | md->disk->major = major; |
364 | * The CSD capacity field is in units of read_blkbits. | 392 | md->disk->first_minor = devidx << MMC_SHIFT; |
365 | * set_capacity takes units of 512 bytes. | 393 | md->disk->fops = &mmc_bdops; |
366 | */ | 394 | md->disk->private_data = md; |
367 | set_capacity(md->disk, card->csd.capacity << (card->csd.read_blkbits - 9)); | 395 | md->disk->queue = md->queue.queue; |
368 | } | 396 | md->disk->driverfs_dev = &card->dev; |
369 | out: | 397 | |
398 | /* | ||
399 | * As discussed on lkml, GENHD_FL_REMOVABLE should: | ||
400 | * | ||
401 | * - be set for removable media with permanent block devices | ||
402 | * - be unset for removable block devices with permanent media | ||
403 | * | ||
404 | * Since MMC block devices clearly fall under the second | ||
405 | * case, we do not set GENHD_FL_REMOVABLE. Userspace | ||
406 | * should use the block device creation/destruction hotplug | ||
407 | * messages to tell when the card is present. | ||
408 | */ | ||
409 | |||
410 | sprintf(md->disk->disk_name, "mmcblk%d", devidx); | ||
411 | sprintf(md->disk->devfs_name, "mmc/blk%d", devidx); | ||
412 | |||
413 | blk_queue_hardsect_size(md->queue.queue, 1 << md->block_bits); | ||
414 | |||
415 | /* | ||
416 | * The CSD capacity field is in units of read_blkbits. | ||
417 | * set_capacity takes units of 512 bytes. | ||
418 | */ | ||
419 | set_capacity(md->disk, card->csd.capacity << (card->csd.read_blkbits - 9)); | ||
370 | return md; | 420 | return md; |
421 | |||
422 | err_putdisk: | ||
423 | put_disk(md->disk); | ||
424 | err_kfree: | ||
425 | kfree(md); | ||
426 | out: | ||
427 | return ERR_PTR(ret); | ||
371 | } | 428 | } |
372 | 429 | ||
373 | static int | 430 | static int |
@@ -403,12 +460,6 @@ static int mmc_blk_probe(struct mmc_card *card) | |||
403 | if (!(card->csd.cmdclass & CCC_BLOCK_READ)) | 460 | if (!(card->csd.cmdclass & CCC_BLOCK_READ)) |
404 | return -ENODEV; | 461 | return -ENODEV; |
405 | 462 | ||
406 | if (card->csd.read_blkbits < 9) { | ||
407 | printk(KERN_WARNING "%s: read blocksize too small (%u)\n", | ||
408 | mmc_card_id(card), 1 << card->csd.read_blkbits); | ||
409 | return -ENODEV; | ||
410 | } | ||
411 | |||
412 | md = mmc_blk_alloc(card); | 463 | md = mmc_blk_alloc(card); |
413 | if (IS_ERR(md)) | 464 | if (IS_ERR(md)) |
414 | return PTR_ERR(md); | 465 | return PTR_ERR(md); |
@@ -419,7 +470,7 @@ static int mmc_blk_probe(struct mmc_card *card) | |||
419 | 470 | ||
420 | printk(KERN_INFO "%s: %s %s %luKiB %s\n", | 471 | printk(KERN_INFO "%s: %s %s %luKiB %s\n", |
421 | md->disk->disk_name, mmc_card_id(card), mmc_card_name(card), | 472 | md->disk->disk_name, mmc_card_id(card), mmc_card_name(card), |
422 | get_capacity(md->disk) >> 1, mmc_blk_readonly(card)?"(ro)":""); | 473 | get_capacity(md->disk) >> 1, md->read_only ? "(ro)" : ""); |
423 | 474 | ||
424 | mmc_set_drvdata(card, md); | 475 | mmc_set_drvdata(card, md); |
425 | add_disk(md->disk); | 476 | add_disk(md->disk); |