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