diff options
author | Pierre Ossman <drzeus@drzeus.cx> | 2006-06-30 05:22:26 -0400 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2006-07-02 11:02:05 -0400 |
commit | 3192a28f7d34ea8f1d0fef8ca5bc0314b5b5bb19 (patch) | |
tree | 8e10e81299096729254d87cb1b7aba94a150c84d /drivers/mmc | |
parent | c7fa9963ee6317b54e85b260791d603ea2feb8e3 (diff) |
[MMC] sdhci: fix interrupt handling
The specification says that interrupts should be cleared before the source is
removed. We should also not set unknown bits.
Signed-off-by: Pierre Ossman <drzeus@drzeus.cx>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'drivers/mmc')
-rw-r--r-- | drivers/mmc/sdhci.c | 80 |
1 files changed, 24 insertions, 56 deletions
diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index 445788159647..b9aa60aed7f0 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c | |||
@@ -124,7 +124,12 @@ static void sdhci_init(struct sdhci_host *host) | |||
124 | 124 | ||
125 | sdhci_reset(host, SDHCI_RESET_ALL); | 125 | sdhci_reset(host, SDHCI_RESET_ALL); |
126 | 126 | ||
127 | intmask = ~(SDHCI_INT_CARD_INT | SDHCI_INT_BUF_EMPTY | SDHCI_INT_BUF_FULL); | 127 | intmask = SDHCI_INT_BUS_POWER | SDHCI_INT_DATA_END_BIT | |
128 | SDHCI_INT_DATA_CRC | SDHCI_INT_DATA_TIMEOUT | SDHCI_INT_INDEX | | ||
129 | SDHCI_INT_END_BIT | SDHCI_INT_CRC | SDHCI_INT_TIMEOUT | | ||
130 | SDHCI_INT_CARD_REMOVE | SDHCI_INT_CARD_INSERT | | ||
131 | SDHCI_INT_BUF_EMPTY | SDHCI_INT_BUF_FULL | | ||
132 | SDHCI_INT_DMA_END | SDHCI_INT_DATA_END | SDHCI_INT_RESPONSE; | ||
128 | 133 | ||
129 | writel(intmask, host->ioaddr + SDHCI_INT_ENABLE); | 134 | writel(intmask, host->ioaddr + SDHCI_INT_ENABLE); |
130 | writel(intmask, host->ioaddr + SDHCI_SIGNAL_ENABLE); | 135 | writel(intmask, host->ioaddr + SDHCI_SIGNAL_ENABLE); |
@@ -360,7 +365,6 @@ static void sdhci_set_transfer_mode(struct sdhci_host *host, | |||
360 | static void sdhci_finish_data(struct sdhci_host *host) | 365 | static void sdhci_finish_data(struct sdhci_host *host) |
361 | { | 366 | { |
362 | struct mmc_data *data; | 367 | struct mmc_data *data; |
363 | u32 intmask; | ||
364 | u16 blocks; | 368 | u16 blocks; |
365 | 369 | ||
366 | BUG_ON(!host->data); | 370 | BUG_ON(!host->data); |
@@ -371,14 +375,6 @@ static void sdhci_finish_data(struct sdhci_host *host) | |||
371 | if (host->flags & SDHCI_USE_DMA) { | 375 | if (host->flags & SDHCI_USE_DMA) { |
372 | pci_unmap_sg(host->chip->pdev, data->sg, data->sg_len, | 376 | pci_unmap_sg(host->chip->pdev, data->sg, data->sg_len, |
373 | (data->flags & MMC_DATA_READ)?PCI_DMA_FROMDEVICE:PCI_DMA_TODEVICE); | 377 | (data->flags & MMC_DATA_READ)?PCI_DMA_FROMDEVICE:PCI_DMA_TODEVICE); |
374 | } else { | ||
375 | intmask = readl(host->ioaddr + SDHCI_SIGNAL_ENABLE); | ||
376 | intmask &= ~(SDHCI_INT_BUF_EMPTY | SDHCI_INT_BUF_FULL); | ||
377 | writel(intmask, host->ioaddr + SDHCI_SIGNAL_ENABLE); | ||
378 | |||
379 | intmask = readl(host->ioaddr + SDHCI_INT_ENABLE); | ||
380 | intmask &= ~(SDHCI_INT_BUF_EMPTY | SDHCI_INT_BUF_FULL); | ||
381 | writel(intmask, host->ioaddr + SDHCI_INT_ENABLE); | ||
382 | } | 378 | } |
383 | 379 | ||
384 | /* | 380 | /* |
@@ -512,31 +508,9 @@ static void sdhci_finish_command(struct sdhci_host *host) | |||
512 | 508 | ||
513 | DBG("Ending cmd (%x)\n", host->cmd->opcode); | 509 | DBG("Ending cmd (%x)\n", host->cmd->opcode); |
514 | 510 | ||
515 | if (host->cmd->data) { | 511 | if (host->cmd->data) |
516 | u32 intmask; | ||
517 | |||
518 | host->data = host->cmd->data; | 512 | host->data = host->cmd->data; |
519 | 513 | else | |
520 | if (!(host->flags & SDHCI_USE_DMA)) { | ||
521 | /* | ||
522 | * Don't enable the interrupts until now to make sure we | ||
523 | * get stable handling of the FIFO. | ||
524 | */ | ||
525 | intmask = readl(host->ioaddr + SDHCI_INT_ENABLE); | ||
526 | intmask |= SDHCI_INT_BUF_EMPTY | SDHCI_INT_BUF_FULL; | ||
527 | writel(intmask, host->ioaddr + SDHCI_INT_ENABLE); | ||
528 | |||
529 | intmask = readl(host->ioaddr + SDHCI_SIGNAL_ENABLE); | ||
530 | intmask |= SDHCI_INT_BUF_EMPTY | SDHCI_INT_BUF_FULL; | ||
531 | writel(intmask, host->ioaddr + SDHCI_SIGNAL_ENABLE); | ||
532 | |||
533 | /* | ||
534 | * The buffer interrupts are to unreliable so we | ||
535 | * start the transfer immediatly. | ||
536 | */ | ||
537 | sdhci_transfer_pio(host); | ||
538 | } | ||
539 | } else | ||
540 | tasklet_schedule(&host->finish_tasklet); | 514 | tasklet_schedule(&host->finish_tasklet); |
541 | 515 | ||
542 | host->cmd = NULL; | 516 | host->cmd = NULL; |
@@ -914,50 +888,44 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id, struct pt_regs *regs) | |||
914 | 888 | ||
915 | DBG("*** %s got interrupt: 0x%08x\n", host->slot_descr, intmask); | 889 | DBG("*** %s got interrupt: 0x%08x\n", host->slot_descr, intmask); |
916 | 890 | ||
917 | if (intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)) | 891 | if (intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)) { |
892 | writel(intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE), | ||
893 | host->ioaddr + SDHCI_INT_STATUS); | ||
918 | tasklet_schedule(&host->card_tasklet); | 894 | tasklet_schedule(&host->card_tasklet); |
895 | } | ||
919 | 896 | ||
920 | if (intmask & SDHCI_INT_CMD_MASK) { | 897 | intmask &= ~(SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE); |
921 | sdhci_cmd_irq(host, intmask & SDHCI_INT_CMD_MASK); | ||
922 | 898 | ||
899 | if (intmask & SDHCI_INT_CMD_MASK) { | ||
923 | writel(intmask & SDHCI_INT_CMD_MASK, | 900 | writel(intmask & SDHCI_INT_CMD_MASK, |
924 | host->ioaddr + SDHCI_INT_STATUS); | 901 | host->ioaddr + SDHCI_INT_STATUS); |
902 | sdhci_cmd_irq(host, intmask & SDHCI_INT_CMD_MASK); | ||
925 | } | 903 | } |
926 | 904 | ||
927 | if (intmask & SDHCI_INT_DATA_MASK) { | 905 | if (intmask & SDHCI_INT_DATA_MASK) { |
928 | sdhci_data_irq(host, intmask & SDHCI_INT_DATA_MASK); | ||
929 | |||
930 | writel(intmask & SDHCI_INT_DATA_MASK, | 906 | writel(intmask & SDHCI_INT_DATA_MASK, |
931 | host->ioaddr + SDHCI_INT_STATUS); | 907 | host->ioaddr + SDHCI_INT_STATUS); |
908 | sdhci_data_irq(host, intmask & SDHCI_INT_DATA_MASK); | ||
932 | } | 909 | } |
933 | 910 | ||
934 | intmask &= ~(SDHCI_INT_CMD_MASK | SDHCI_INT_DATA_MASK); | 911 | intmask &= ~(SDHCI_INT_CMD_MASK | SDHCI_INT_DATA_MASK); |
935 | 912 | ||
936 | if (intmask & SDHCI_INT_CARD_INT) { | ||
937 | printk(KERN_ERR "%s: Unexpected card interrupt. Please " | ||
938 | "report this to " BUGMAIL ".\n", | ||
939 | mmc_hostname(host->mmc)); | ||
940 | sdhci_dumpregs(host); | ||
941 | } | ||
942 | |||
943 | if (intmask & SDHCI_INT_BUS_POWER) { | 913 | if (intmask & SDHCI_INT_BUS_POWER) { |
944 | printk(KERN_ERR "%s: Unexpected bus power interrupt. Please " | 914 | printk(KERN_ERR "%s: Card is consuming too much power!\n", |
945 | "report this to " BUGMAIL ".\n", | ||
946 | mmc_hostname(host->mmc)); | 915 | mmc_hostname(host->mmc)); |
947 | sdhci_dumpregs(host); | 916 | writel(SDHCI_INT_BUS_POWER, host->ioaddr + SDHCI_INT_STATUS); |
948 | } | 917 | } |
949 | 918 | ||
950 | if (intmask & SDHCI_INT_ACMD12ERR) { | 919 | intmask &= SDHCI_INT_BUS_POWER; |
951 | printk(KERN_ERR "%s: Unexpected auto CMD12 error. Please " | 920 | |
921 | if (intmask) { | ||
922 | printk(KERN_ERR "%s: Unexpected interrupt 0x%08x. Please " | ||
952 | "report this to " BUGMAIL ".\n", | 923 | "report this to " BUGMAIL ".\n", |
953 | mmc_hostname(host->mmc)); | 924 | mmc_hostname(host->mmc), intmask); |
954 | sdhci_dumpregs(host); | 925 | sdhci_dumpregs(host); |
955 | 926 | ||
956 | writew(~0, host->ioaddr + SDHCI_ACMD12_ERR); | ||
957 | } | ||
958 | |||
959 | if (intmask) | ||
960 | writel(intmask, host->ioaddr + SDHCI_INT_STATUS); | 927 | writel(intmask, host->ioaddr + SDHCI_INT_STATUS); |
928 | } | ||
961 | 929 | ||
962 | result = IRQ_HANDLED; | 930 | result = IRQ_HANDLED; |
963 | 931 | ||