diff options
Diffstat (limited to 'drivers/mmc/core/mmc_ops.c')
-rw-r--r-- | drivers/mmc/core/mmc_ops.c | 101 |
1 files changed, 101 insertions, 0 deletions
diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c index 326447c9ede8..60842f878ded 100644 --- a/drivers/mmc/core/mmc_ops.c +++ b/drivers/mmc/core/mmc_ops.c | |||
@@ -462,3 +462,104 @@ int mmc_send_status(struct mmc_card *card, u32 *status) | |||
462 | return 0; | 462 | return 0; |
463 | } | 463 | } |
464 | 464 | ||
465 | static int | ||
466 | mmc_send_bus_test(struct mmc_card *card, struct mmc_host *host, u8 opcode, | ||
467 | u8 len) | ||
468 | { | ||
469 | struct mmc_request mrq; | ||
470 | struct mmc_command cmd; | ||
471 | struct mmc_data data; | ||
472 | struct scatterlist sg; | ||
473 | u8 *data_buf; | ||
474 | u8 *test_buf; | ||
475 | int i, err; | ||
476 | static u8 testdata_8bit[8] = { 0x55, 0xaa, 0, 0, 0, 0, 0, 0 }; | ||
477 | static u8 testdata_4bit[4] = { 0x5a, 0, 0, 0 }; | ||
478 | |||
479 | /* dma onto stack is unsafe/nonportable, but callers to this | ||
480 | * routine normally provide temporary on-stack buffers ... | ||
481 | */ | ||
482 | data_buf = kmalloc(len, GFP_KERNEL); | ||
483 | if (!data_buf) | ||
484 | return -ENOMEM; | ||
485 | |||
486 | if (len == 8) | ||
487 | test_buf = testdata_8bit; | ||
488 | else if (len == 4) | ||
489 | test_buf = testdata_4bit; | ||
490 | else { | ||
491 | printk(KERN_ERR "%s: Invalid bus_width %d\n", | ||
492 | mmc_hostname(host), len); | ||
493 | kfree(data_buf); | ||
494 | return -EINVAL; | ||
495 | } | ||
496 | |||
497 | if (opcode == MMC_BUS_TEST_W) | ||
498 | memcpy(data_buf, test_buf, len); | ||
499 | |||
500 | memset(&mrq, 0, sizeof(struct mmc_request)); | ||
501 | memset(&cmd, 0, sizeof(struct mmc_command)); | ||
502 | memset(&data, 0, sizeof(struct mmc_data)); | ||
503 | |||
504 | mrq.cmd = &cmd; | ||
505 | mrq.data = &data; | ||
506 | cmd.opcode = opcode; | ||
507 | cmd.arg = 0; | ||
508 | |||
509 | /* NOTE HACK: the MMC_RSP_SPI_R1 is always correct here, but we | ||
510 | * rely on callers to never use this with "native" calls for reading | ||
511 | * CSD or CID. Native versions of those commands use the R2 type, | ||
512 | * not R1 plus a data block. | ||
513 | */ | ||
514 | cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC; | ||
515 | |||
516 | data.blksz = len; | ||
517 | data.blocks = 1; | ||
518 | if (opcode == MMC_BUS_TEST_R) | ||
519 | data.flags = MMC_DATA_READ; | ||
520 | else | ||
521 | data.flags = MMC_DATA_WRITE; | ||
522 | |||
523 | data.sg = &sg; | ||
524 | data.sg_len = 1; | ||
525 | sg_init_one(&sg, data_buf, len); | ||
526 | mmc_wait_for_req(host, &mrq); | ||
527 | err = 0; | ||
528 | if (opcode == MMC_BUS_TEST_R) { | ||
529 | for (i = 0; i < len / 4; i++) | ||
530 | if ((test_buf[i] ^ data_buf[i]) != 0xff) { | ||
531 | err = -EIO; | ||
532 | break; | ||
533 | } | ||
534 | } | ||
535 | kfree(data_buf); | ||
536 | |||
537 | if (cmd.error) | ||
538 | return cmd.error; | ||
539 | if (data.error) | ||
540 | return data.error; | ||
541 | |||
542 | return err; | ||
543 | } | ||
544 | |||
545 | int mmc_bus_test(struct mmc_card *card, u8 bus_width) | ||
546 | { | ||
547 | int err, width; | ||
548 | |||
549 | if (bus_width == MMC_BUS_WIDTH_8) | ||
550 | width = 8; | ||
551 | else if (bus_width == MMC_BUS_WIDTH_4) | ||
552 | width = 4; | ||
553 | else if (bus_width == MMC_BUS_WIDTH_1) | ||
554 | return 0; /* no need for test */ | ||
555 | else | ||
556 | return -EINVAL; | ||
557 | |||
558 | /* | ||
559 | * Ignore errors from BUS_TEST_W. BUS_TEST_R will fail if there | ||
560 | * is a problem. This improves chances that the test will work. | ||
561 | */ | ||
562 | mmc_send_bus_test(card, card->host, MMC_BUS_TEST_W, width); | ||
563 | err = mmc_send_bus_test(card, card->host, MMC_BUS_TEST_R, width); | ||
564 | return err; | ||
565 | } | ||