diff options
author | Aries Lee <arieslee@jmicron.com> | 2010-12-15 02:14:24 -0500 |
---|---|---|
committer | Chris Ball <cjb@laptop.org> | 2011-01-08 23:52:09 -0500 |
commit | 22113efd00491310da802f3b1a9a66cfcf415fac (patch) | |
tree | 1faf6e99a591f9b6856bab6c8318eeeacb076051 /drivers/mmc | |
parent | e6f29a8dc1602e170daf955233891a9130573a55 (diff) |
mmc: Test bus-width for old MMC devices
Some old MMC devices fail with the 4/8 bits the driver tries to use
exclusively. This patch adds a test for the given bus setup and falls
back to the lower bit mode (until 1-bit mode) when the test fails.
[Major rework and refactoring by tiwai]
[Quirk addition and many fixes by prakity]
Signed-off-by: Aries Lee <arieslee@jmicron.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Signed-off-by: Philip Rakity <prakity@marvell.com>
Tested-by: Philip Rakity <prakity@marvell.com>
Signed-off-by: Chris Ball <cjb@laptop.org>
Diffstat (limited to 'drivers/mmc')
-rw-r--r-- | drivers/mmc/core/mmc.c | 76 | ||||
-rw-r--r-- | drivers/mmc/core/mmc_ops.c | 101 | ||||
-rw-r--r-- | drivers/mmc/core/mmc_ops.h | 1 | ||||
-rw-r--r-- | drivers/mmc/host/sdhci.c | 7 | ||||
-rw-r--r-- | drivers/mmc/host/sdhci.h | 1 |
5 files changed, 156 insertions, 30 deletions
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 76bb621e9aa9..1d8409fcf155 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c | |||
@@ -534,39 +534,57 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, | |||
534 | */ | 534 | */ |
535 | if ((card->csd.mmca_vsn >= CSD_SPEC_VER_4) && | 535 | if ((card->csd.mmca_vsn >= CSD_SPEC_VER_4) && |
536 | (host->caps & (MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA))) { | 536 | (host->caps & (MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA))) { |
537 | unsigned ext_csd_bit, bus_width; | 537 | static unsigned ext_csd_bits[][2] = { |
538 | 538 | { EXT_CSD_BUS_WIDTH_8, EXT_CSD_DDR_BUS_WIDTH_8 }, | |
539 | if (host->caps & MMC_CAP_8_BIT_DATA) { | 539 | { EXT_CSD_BUS_WIDTH_4, EXT_CSD_DDR_BUS_WIDTH_4 }, |
540 | if (ddr) | 540 | { EXT_CSD_BUS_WIDTH_1, EXT_CSD_BUS_WIDTH_1 }, |
541 | ext_csd_bit = EXT_CSD_DDR_BUS_WIDTH_8; | 541 | }; |
542 | else | 542 | static unsigned bus_widths[] = { |
543 | ext_csd_bit = EXT_CSD_BUS_WIDTH_8; | 543 | MMC_BUS_WIDTH_8, |
544 | bus_width = MMC_BUS_WIDTH_8; | 544 | MMC_BUS_WIDTH_4, |
545 | } else { | 545 | MMC_BUS_WIDTH_1 |
546 | if (ddr) | 546 | }; |
547 | ext_csd_bit = EXT_CSD_DDR_BUS_WIDTH_4; | 547 | unsigned idx, bus_width = 0; |
548 | else | 548 | |
549 | ext_csd_bit = EXT_CSD_BUS_WIDTH_4; | 549 | if (host->caps & MMC_CAP_8_BIT_DATA) |
550 | bus_width = MMC_BUS_WIDTH_4; | 550 | idx = 0; |
551 | else | ||
552 | idx = 1; | ||
553 | for (; idx < ARRAY_SIZE(bus_widths); idx++) { | ||
554 | bus_width = bus_widths[idx]; | ||
555 | if (bus_width == MMC_BUS_WIDTH_1) | ||
556 | ddr = 0; /* no DDR for 1-bit width */ | ||
557 | err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, | ||
558 | EXT_CSD_BUS_WIDTH, | ||
559 | ext_csd_bits[idx][0]); | ||
560 | if (!err) { | ||
561 | /* | ||
562 | * If controller can't handle bus width test, | ||
563 | * use the highest bus width to maintain | ||
564 | * compatibility with previous MMC behavior. | ||
565 | */ | ||
566 | if (!(host->caps & MMC_CAP_BUS_WIDTH_TEST)) | ||
567 | break; | ||
568 | mmc_set_bus_width_ddr(card->host, | ||
569 | bus_width, MMC_SDR_MODE); | ||
570 | err = mmc_bus_test(card, bus_width); | ||
571 | if (!err) | ||
572 | break; | ||
573 | } | ||
551 | } | 574 | } |
552 | 575 | ||
553 | err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, | 576 | if (!err && ddr) { |
554 | EXT_CSD_BUS_WIDTH, ext_csd_bit); | 577 | err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, |
555 | 578 | EXT_CSD_BUS_WIDTH, | |
556 | if (err && err != -EBADMSG) | 579 | ext_csd_bits[idx][1]); |
557 | goto free_card; | 580 | } |
558 | |||
559 | if (err) { | 581 | if (err) { |
560 | printk(KERN_WARNING "%s: switch to bus width %d ddr %d " | 582 | printk(KERN_WARNING "%s: switch to bus width %d ddr %d " |
561 | "failed\n", mmc_hostname(card->host), | 583 | "failed\n", mmc_hostname(card->host), |
562 | 1 << bus_width, ddr); | 584 | 1 << bus_width, ddr); |
563 | err = 0; | 585 | goto free_card; |
564 | } else { | 586 | } else if (ddr) { |
565 | if (ddr) | 587 | mmc_card_set_ddr_mode(card); |
566 | mmc_card_set_ddr_mode(card); | ||
567 | else | ||
568 | ddr = MMC_SDR_MODE; | ||
569 | |||
570 | mmc_set_bus_width_ddr(card->host, bus_width, ddr); | 588 | mmc_set_bus_width_ddr(card->host, bus_width, ddr); |
571 | } | 589 | } |
572 | } | 590 | } |
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 | } | ||
diff --git a/drivers/mmc/core/mmc_ops.h b/drivers/mmc/core/mmc_ops.h index 653eb8e84178..e6d44b8a18db 100644 --- a/drivers/mmc/core/mmc_ops.h +++ b/drivers/mmc/core/mmc_ops.h | |||
@@ -26,6 +26,7 @@ int mmc_send_cid(struct mmc_host *host, u32 *cid); | |||
26 | int mmc_spi_read_ocr(struct mmc_host *host, int highcap, u32 *ocrp); | 26 | int mmc_spi_read_ocr(struct mmc_host *host, int highcap, u32 *ocrp); |
27 | int mmc_spi_set_crc(struct mmc_host *host, int use_crc); | 27 | int mmc_spi_set_crc(struct mmc_host *host, int use_crc); |
28 | int mmc_card_sleepawake(struct mmc_host *host, int sleep); | 28 | int mmc_card_sleepawake(struct mmc_host *host, int sleep); |
29 | int mmc_bus_test(struct mmc_card *card, u8 bus_width); | ||
29 | 30 | ||
30 | #endif | 31 | #endif |
31 | 32 | ||
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 55698864c2cd..d5febe584b05 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c | |||
@@ -23,6 +23,7 @@ | |||
23 | 23 | ||
24 | #include <linux/leds.h> | 24 | #include <linux/leds.h> |
25 | 25 | ||
26 | #include <linux/mmc/mmc.h> | ||
26 | #include <linux/mmc/host.h> | 27 | #include <linux/mmc/host.h> |
27 | 28 | ||
28 | #include "sdhci.h" | 29 | #include "sdhci.h" |
@@ -1521,7 +1522,11 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask) | |||
1521 | 1522 | ||
1522 | if (intmask & SDHCI_INT_DATA_TIMEOUT) | 1523 | if (intmask & SDHCI_INT_DATA_TIMEOUT) |
1523 | host->data->error = -ETIMEDOUT; | 1524 | host->data->error = -ETIMEDOUT; |
1524 | else if (intmask & (SDHCI_INT_DATA_CRC | SDHCI_INT_DATA_END_BIT)) | 1525 | else if (intmask & SDHCI_INT_DATA_END_BIT) |
1526 | host->data->error = -EILSEQ; | ||
1527 | else if ((intmask & SDHCI_INT_DATA_CRC) && | ||
1528 | SDHCI_GET_CMD(sdhci_readw(host, SDHCI_COMMAND)) | ||
1529 | != MMC_BUS_TEST_R) | ||
1525 | host->data->error = -EILSEQ; | 1530 | host->data->error = -EILSEQ; |
1526 | else if (intmask & SDHCI_INT_ADMA_ERROR) { | 1531 | else if (intmask & SDHCI_INT_ADMA_ERROR) { |
1527 | printk(KERN_ERR "%s: ADMA error\n", mmc_hostname(host->mmc)); | 1532 | printk(KERN_ERR "%s: ADMA error\n", mmc_hostname(host->mmc)); |
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index 1efe7dc5255b..6e0969e40650 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h | |||
@@ -52,6 +52,7 @@ | |||
52 | #define SDHCI_CMD_RESP_SHORT_BUSY 0x03 | 52 | #define SDHCI_CMD_RESP_SHORT_BUSY 0x03 |
53 | 53 | ||
54 | #define SDHCI_MAKE_CMD(c, f) (((c & 0xff) << 8) | (f & 0xff)) | 54 | #define SDHCI_MAKE_CMD(c, f) (((c & 0xff) << 8) | (f & 0xff)) |
55 | #define SDHCI_GET_CMD(c) ((c>>8) & 0x3f) | ||
55 | 56 | ||
56 | #define SDHCI_RESPONSE 0x10 | 57 | #define SDHCI_RESPONSE 0x10 |
57 | 58 | ||