diff options
Diffstat (limited to 'drivers/mmc/host/sdhci.c')
-rw-r--r-- | drivers/mmc/host/sdhci.c | 71 |
1 files changed, 62 insertions, 9 deletions
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 4d010a984be..ed2c89254df 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -73,6 +73,11 @@ static void sdhci_dumpregs(struct sdhci_host *host) readl(host->ioaddr + SDHCI_CAPABILITIES), readl(host->ioaddr + SDHCI_MAX_CURRENT)); + if (host->flags & SDHCI_USE_ADMA) + printk(KERN_DEBUG DRIVER_NAME ": ADMA Err: 0x%08x | ADMA Ptr: 0x%08x\n", + readl(host->ioaddr + SDHCI_ADMA_ERROR), + readl(host->ioaddr + SDHCI_ADMA_ADDRESS)); + printk(KERN_DEBUG DRIVER_NAME ": ===========================================\n"); } @@ -731,6 +736,23 @@ static void sdhci_set_transfer_mode(struct sdhci_host *host, writew(mode, host->ioaddr + SDHCI_TRANSFER_MODE); } +static void shdci_check_dma_overrun(struct sdhci_host *host, struct mmc_data *data) +{ + u32 dma_pos = readl(host->ioaddr + SDHCI_DMA_ADDRESS); + u32 dma_start = sg_dma_address(data->sg); + u32 dma_end = dma_start + data->sg->length; + + /* Test whether we ended up moving more data than + * was originally requested. */ + + if (dma_pos <= dma_end) + return; + + printk(KERN_ERR "%s: dma overrun, dma %08x, req %08x..%08x\n", + mmc_hostname(host->mmc), dma_pos, + dma_start, dma_end); +} + static void sdhci_finish_data(struct sdhci_host *host) { struct mmc_data *data; @@ -744,6 +766,8 @@ static void sdhci_finish_data(struct sdhci_host *host) if (host->flags & SDHCI_USE_ADMA) sdhci_adma_table_post(host, data); else { + shdci_check_dma_overrun(host, data); + dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len, (data->flags & MMC_DATA_READ) ? DMA_FROM_DEVICE : DMA_TO_DEVICE); @@ -883,13 +907,18 @@ static void sdhci_finish_command(struct sdhci_host *host) static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock) { + if (clock == host->clock) + return; + + host->ops->change_clock(host, clock); +} + +void sdhci_change_clock(struct sdhci_host *host, unsigned int clock) +{ int div; u16 clk; unsigned long timeout; - if (clock == host->clock) - return; - writew(0, host->ioaddr + SDHCI_CLOCK_CONTROL); if (clock == 0) @@ -926,6 +955,8 @@ out: host->clock = clock; } +EXPORT_SYMBOL_GPL(sdhci_set_clock); + static void sdhci_set_power(struct sdhci_host *host, unsigned short power) { u8 pwr; @@ -1033,6 +1064,9 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) sdhci_init(host); } + if (host->ops->set_ios) + host->ops->set_ios(host, ios); + sdhci_set_clock(host, ios->clock); if (ios->power_mode == MMC_POWER_OFF) @@ -1283,11 +1317,24 @@ static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask) * controllers. */ if (host->cmd->flags & MMC_RSP_BUSY) { + u32 present; + if (host->cmd->data) DBG("Cannot wait for busy signal when also " "doing a data transfer"); - else + else if (!(host->quirks & SDHCI_QUIRK_NO_TCIRQ_ON_NOT_BUSY)) return; + + /* The Samsung SDHCI does not seem to provide an INT_DATA_END + * when the system goes non-busy, so check the state of the + * transfer by reading SDHCI_PRESENT_STATE to see if the + * controller is ready + */ + + present = readl(host->ioaddr + SDHCI_PRESENT_STATE); + DBG("busy? present %08x, intstat %08x\n", present, intmask); + + /* fall through and take the SDHCI_INT_RESPONSE */ } if (intmask & SDHCI_INT_RESPONSE) @@ -1604,17 +1651,23 @@ int sdhci_add_host(struct sdhci_host *host) mmc_dev(host->mmc)->dma_mask = &host->dma_mask; } - host->max_clk = - (caps & SDHCI_CLOCK_BASE_MASK) >> SDHCI_CLOCK_BASE_SHIFT; + if (host->ops->get_max_clock) + host->max_clk = host->ops->get_max_clock(host); + else { + host->max_clk = (caps & SDHCI_CLOCK_BASE_MASK) >> SDHCI_CLOCK_BASE_SHIFT; + host->max_clk *= 1000000; + } if (host->max_clk == 0) { printk(KERN_ERR "%s: Hardware doesn't specify base clock " "frequency.\n", mmc_hostname(mmc)); return -ENODEV; } - host->max_clk *= 1000000; - host->timeout_clk = - (caps & SDHCI_TIMEOUT_CLK_MASK) >> SDHCI_TIMEOUT_CLK_SHIFT; + if (host->ops->get_timeout_clock) + host->timeout_clk = host->ops->get_timeout_clock(host); + else + host->timeout_clk = + (caps & SDHCI_TIMEOUT_CLK_MASK) >> SDHCI_TIMEOUT_CLK_SHIFT; if (host->timeout_clk == 0) { printk(KERN_ERR "%s: Hardware doesn't specify timeout clock " "frequency.\n", mmc_hostname(mmc)); |