diff options
author | Pierre Ossman <drzeus@drzeus.cx> | 2006-06-30 05:22:25 -0400 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2006-07-02 11:02:04 -0400 |
commit | 1c8cde92fa5c57daa9ff58d970ca6374f8d484a2 (patch) | |
tree | 96ddfe6071bcede86521fda46c0eafd27c4689c1 /drivers/mmc | |
parent | e16514d8d86ecbde18a2a7495cf028861b34c157 (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')
-rw-r--r-- | drivers/mmc/sdhci.c | 47 | ||||
-rw-r--r-- | drivers/mmc/sdhci.h | 4 |
2 files changed, 48 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 | ||
136 | static void sdhci_activate_led(struct sdhci_host *host) | 133 | static void sdhci_activate_led(struct sdhci_host *host) |
@@ -274,6 +271,8 @@ static void sdhci_transfer_pio(struct sdhci_host *host) | |||
274 | static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data) | 271 | static 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 | */ |
diff --git a/drivers/mmc/sdhci.h b/drivers/mmc/sdhci.h index aed4abd37bfd..a8f45215ef3a 100644 --- a/drivers/mmc/sdhci.h +++ b/drivers/mmc/sdhci.h | |||
@@ -125,6 +125,9 @@ | |||
125 | /* 3E-3F reserved */ | 125 | /* 3E-3F reserved */ |
126 | 126 | ||
127 | #define SDHCI_CAPABILITIES 0x40 | 127 | #define SDHCI_CAPABILITIES 0x40 |
128 | #define SDHCI_TIMEOUT_CLK_MASK 0x0000003F | ||
129 | #define SDHCI_TIMEOUT_CLK_SHIFT 0 | ||
130 | #define SDHCI_TIMEOUT_CLK_UNIT 0x00000080 | ||
128 | #define SDHCI_CLOCK_BASE_MASK 0x00003F00 | 131 | #define SDHCI_CLOCK_BASE_MASK 0x00003F00 |
129 | #define SDHCI_CLOCK_BASE_SHIFT 8 | 132 | #define SDHCI_CLOCK_BASE_SHIFT 8 |
130 | #define SDHCI_CAN_DO_DMA 0x00400000 | 133 | #define SDHCI_CAN_DO_DMA 0x00400000 |
@@ -156,6 +159,7 @@ struct sdhci_host { | |||
156 | #define SDHCI_USE_DMA (1<<0) | 159 | #define SDHCI_USE_DMA (1<<0) |
157 | 160 | ||
158 | unsigned int max_clk; /* Max possible freq (MHz) */ | 161 | unsigned int max_clk; /* Max possible freq (MHz) */ |
162 | unsigned int timeout_clk; /* Timeout freq (KHz) */ | ||
159 | 163 | ||
160 | unsigned int clock; /* Current clock (MHz) */ | 164 | unsigned int clock; /* Current clock (MHz) */ |
161 | unsigned short power; /* Current voltage */ | 165 | unsigned short power; /* Current voltage */ |