aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mmc/sdhci.c
diff options
context:
space:
mode:
authorPierre Ossman <drzeus@drzeus.cx>2006-06-30 05:22:25 -0400
committerRussell King <rmk+kernel@arm.linux.org.uk>2006-07-02 11:02:04 -0400
commit1c8cde92fa5c57daa9ff58d970ca6374f8d484a2 (patch)
tree96ddfe6071bcede86521fda46c0eafd27c4689c1 /drivers/mmc/sdhci.c
parente16514d8d86ecbde18a2a7495cf028861b34c157 (diff)
[MMC] sdhci: proper timeout handling
Use the give timeout clock and calculate a proper timeout instead of using the maximum at all times. 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/sdhci.c')
-rw-r--r--drivers/mmc/sdhci.c47
1 files changed, 44 insertions, 3 deletions
diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c
index 77b7db27b555..877226e2ffae 100644
--- a/drivers/mmc/sdhci.c
+++ b/drivers/mmc/sdhci.c
@@ -128,9 +128,6 @@ static void sdhci_init(struct sdhci_host *host)
128 128
129 writel(intmask, host->ioaddr + SDHCI_INT_ENABLE); 129 writel(intmask, host->ioaddr + SDHCI_INT_ENABLE);
130 writel(intmask, host->ioaddr + SDHCI_SIGNAL_ENABLE); 130 writel(intmask, host->ioaddr + SDHCI_SIGNAL_ENABLE);
131
132 /* This is unknown magic. */
133 writeb(0xE, host->ioaddr + SDHCI_TIMEOUT_CONTROL);
134} 131}
135 132
136static void sdhci_activate_led(struct sdhci_host *host) 133static void sdhci_activate_led(struct sdhci_host *host)
@@ -274,6 +271,8 @@ static void sdhci_transfer_pio(struct sdhci_host *host)
274static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data) 271static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data)
275{ 272{
276 u16 mode; 273 u16 mode;
274 u8 count;
275 unsigned target_timeout, current_timeout;
277 276
278 WARN_ON(host->data); 277 WARN_ON(host->data);
279 278
@@ -287,6 +286,37 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data)
287 DBG("tsac %d ms nsac %d clk\n", 286 DBG("tsac %d ms nsac %d clk\n",
288 data->timeout_ns / 1000000, data->timeout_clks); 287 data->timeout_ns / 1000000, data->timeout_clks);
289 288
289 /* timeout in us */
290 target_timeout = data->timeout_ns / 1000 +
291 data->timeout_clks / host->clock;
292
293 /*
294 * Figure out needed cycles.
295 * We do this in steps in order to fit inside a 32 bit int.
296 * The first step is the minimum timeout, which will have a
297 * minimum resolution of 6 bits:
298 * (1) 2^13*1000 > 2^22,
299 * (2) host->timeout_clk < 2^16
300 * =>
301 * (1) / (2) > 2^6
302 */
303 count = 0;
304 current_timeout = (1 << 13) * 1000 / host->timeout_clk;
305 while (current_timeout < target_timeout) {
306 count++;
307 current_timeout <<= 1;
308 if (count >= 0xF)
309 break;
310 }
311
312 if (count >= 0xF) {
313 printk(KERN_WARNING "%s: Too large timeout requested!\n",
314 mmc_hostname(host->mmc));
315 count = 0xE;
316 }
317
318 writeb(count, host->ioaddr + SDHCI_TIMEOUT_CONTROL);
319
290 mode = SDHCI_TRNS_BLK_CNT_EN; 320 mode = SDHCI_TRNS_BLK_CNT_EN;
291 if (data->blocks > 1) 321 if (data->blocks > 1)
292 mode |= SDHCI_TRNS_MULTI; 322 mode |= SDHCI_TRNS_MULTI;
@@ -1096,6 +1126,17 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot)
1096 } 1126 }
1097 host->max_clk *= 1000000; 1127 host->max_clk *= 1000000;
1098 1128
1129 host->timeout_clk =
1130 (caps & SDHCI_TIMEOUT_CLK_MASK) >> SDHCI_TIMEOUT_CLK_SHIFT;
1131 if (host->timeout_clk == 0) {
1132 printk(KERN_ERR "%s: Hardware doesn't specify timeout clock "
1133 "frequency.\n", host->slot_descr);
1134 ret = -ENODEV;
1135 goto unmap;
1136 }
1137 if (caps & SDHCI_TIMEOUT_CLK_UNIT)
1138 host->timeout_clk *= 1000;
1139
1099 /* 1140 /*
1100 * Set host parameters. 1141 * Set host parameters.
1101 */ 1142 */