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 | |
| 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>
| -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 | ||
