aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorAries Lee <arieslee@jmicron.com>2010-12-15 02:14:24 -0500
committerChris Ball <cjb@laptop.org>2011-01-08 23:52:09 -0500
commit22113efd00491310da802f3b1a9a66cfcf415fac (patch)
tree1faf6e99a591f9b6856bab6c8318eeeacb076051 /drivers
parente6f29a8dc1602e170daf955233891a9130573a55 (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')
-rw-r--r--drivers/mmc/core/mmc.c76
-rw-r--r--drivers/mmc/core/mmc_ops.c101
-rw-r--r--drivers/mmc/core/mmc_ops.h1
-rw-r--r--drivers/mmc/host/sdhci.c7
-rw-r--r--drivers/mmc/host/sdhci.h1
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
465static int
466mmc_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
545int 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);
26int mmc_spi_read_ocr(struct mmc_host *host, int highcap, u32 *ocrp); 26int mmc_spi_read_ocr(struct mmc_host *host, int highcap, u32 *ocrp);
27int mmc_spi_set_crc(struct mmc_host *host, int use_crc); 27int mmc_spi_set_crc(struct mmc_host *host, int use_crc);
28int mmc_card_sleepawake(struct mmc_host *host, int sleep); 28int mmc_card_sleepawake(struct mmc_host *host, int sleep);
29int 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