aboutsummaryrefslogtreecommitdiff
path: root/drivers/mmc/host/sdhci.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mmc/host/sdhci.c')
-rw-r--r--drivers/mmc/host/sdhci.c71
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));