diff options
author | Pierre Ossman <drzeus@drzeus.cx> | 2008-07-04 18:25:15 -0400 |
---|---|---|
committer | Pierre Ossman <drzeus@drzeus.cx> | 2008-07-15 08:14:39 -0400 |
commit | ee53ab5d73998e502801c024a08de2c39a92c52a (patch) | |
tree | bbee55e0cfffa4837918b7ada2f031f7dab3fcbb | |
parent | 22606405894a3ca5796eb4454a4b83af611fd201 (diff) |
sdhci: make workaround for timeout bug more general
Give the quirk for broken timeout handling a better chance of handling
more controllers by simply classifying the system as broken and setting
a fixed value.
Signed-off-by: Pierre Ossman <drzeus@drzeus.cx>
-rw-r--r-- | drivers/mmc/host/sdhci-pci.c | 2 | ||||
-rw-r--r-- | drivers/mmc/host/sdhci.c | 50 | ||||
-rw-r--r-- | drivers/mmc/host/sdhci.h | 4 |
3 files changed, 33 insertions, 23 deletions
diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c index 5094fe805764..856bb2b2837e 100644 --- a/drivers/mmc/host/sdhci-pci.c +++ b/drivers/mmc/host/sdhci-pci.c | |||
@@ -98,7 +98,7 @@ static const struct sdhci_pci_fixes sdhci_ene_714 = { | |||
98 | 98 | ||
99 | static const struct sdhci_pci_fixes sdhci_cafe = { | 99 | static const struct sdhci_pci_fixes sdhci_cafe = { |
100 | .quirks = SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER | | 100 | .quirks = SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER | |
101 | SDHCI_QUIRK_INCR_TIMEOUT_CONTROL, | 101 | SDHCI_QUIRK_BROKEN_TIMEOUT_VAL, |
102 | }; | 102 | }; |
103 | 103 | ||
104 | static const struct sdhci_pci_fixes sdhci_jmicron = { | 104 | static const struct sdhci_pci_fixes sdhci_jmicron = { |
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 8ce01d434ea8..95b081a9967b 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c | |||
@@ -314,23 +314,19 @@ static void sdhci_transfer_pio(struct sdhci_host *host) | |||
314 | DBG("PIO transfer complete.\n"); | 314 | DBG("PIO transfer complete.\n"); |
315 | } | 315 | } |
316 | 316 | ||
317 | static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data) | 317 | static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_data *data) |
318 | { | 318 | { |
319 | u8 count; | 319 | u8 count; |
320 | unsigned target_timeout, current_timeout; | 320 | unsigned target_timeout, current_timeout; |
321 | 321 | ||
322 | WARN_ON(host->data); | 322 | /* |
323 | 323 | * If the host controller provides us with an incorrect timeout | |
324 | if (data == NULL) | 324 | * value, just skip the check and use 0xE. The hardware may take |
325 | return; | 325 | * longer to time out, but that's much better than having a too-short |
326 | 326 | * timeout value. | |
327 | /* Sanity checks */ | 327 | */ |
328 | BUG_ON(data->blksz * data->blocks > 524288); | 328 | if ((host->quirks & SDHCI_QUIRK_BROKEN_TIMEOUT_VAL)) |
329 | BUG_ON(data->blksz > host->mmc->max_blk_size); | 329 | return 0xE; |
330 | BUG_ON(data->blocks > 65535); | ||
331 | |||
332 | host->data = data; | ||
333 | host->data_early = 0; | ||
334 | 330 | ||
335 | /* timeout in us */ | 331 | /* timeout in us */ |
336 | target_timeout = data->timeout_ns / 1000 + | 332 | target_timeout = data->timeout_ns / 1000 + |
@@ -355,19 +351,33 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data) | |||
355 | break; | 351 | break; |
356 | } | 352 | } |
357 | 353 | ||
358 | /* | ||
359 | * Compensate for an off-by-one error in the CaFe hardware; otherwise, | ||
360 | * a too-small count gives us interrupt timeouts. | ||
361 | */ | ||
362 | if ((host->quirks & SDHCI_QUIRK_INCR_TIMEOUT_CONTROL)) | ||
363 | count++; | ||
364 | |||
365 | if (count >= 0xF) { | 354 | if (count >= 0xF) { |
366 | printk(KERN_WARNING "%s: Too large timeout requested!\n", | 355 | printk(KERN_WARNING "%s: Too large timeout requested!\n", |
367 | mmc_hostname(host->mmc)); | 356 | mmc_hostname(host->mmc)); |
368 | count = 0xE; | 357 | count = 0xE; |
369 | } | 358 | } |
370 | 359 | ||
360 | return count; | ||
361 | } | ||
362 | |||
363 | static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data) | ||
364 | { | ||
365 | u8 count; | ||
366 | |||
367 | WARN_ON(host->data); | ||
368 | |||
369 | if (data == NULL) | ||
370 | return; | ||
371 | |||
372 | /* Sanity checks */ | ||
373 | BUG_ON(data->blksz * data->blocks > 524288); | ||
374 | BUG_ON(data->blksz > host->mmc->max_blk_size); | ||
375 | BUG_ON(data->blocks > 65535); | ||
376 | |||
377 | host->data = data; | ||
378 | host->data_early = 0; | ||
379 | |||
380 | count = sdhci_calc_timeout(host, data); | ||
371 | writeb(count, host->ioaddr + SDHCI_TIMEOUT_CONTROL); | 381 | writeb(count, host->ioaddr + SDHCI_TIMEOUT_CONTROL); |
372 | 382 | ||
373 | if (host->flags & SDHCI_USE_DMA) | 383 | if (host->flags & SDHCI_USE_DMA) |
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index 22fc258b12aa..7ce12f3e7452 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h | |||
@@ -178,8 +178,8 @@ struct sdhci_host { | |||
178 | #define SDHCI_QUIRK_RESET_AFTER_REQUEST (1<<8) | 178 | #define SDHCI_QUIRK_RESET_AFTER_REQUEST (1<<8) |
179 | /* Controller needs voltage and power writes to happen separately */ | 179 | /* Controller needs voltage and power writes to happen separately */ |
180 | #define SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER (1<<9) | 180 | #define SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER (1<<9) |
181 | /* Controller has an off-by-one issue with timeout value */ | 181 | /* Controller provides an incorrect timeout value for transfers */ |
182 | #define SDHCI_QUIRK_INCR_TIMEOUT_CONTROL (1<<10) | 182 | #define SDHCI_QUIRK_BROKEN_TIMEOUT_VAL (1<<10) |
183 | 183 | ||
184 | int irq; /* Device IRQ */ | 184 | int irq; /* Device IRQ */ |
185 | void __iomem * ioaddr; /* Mapped address */ | 185 | void __iomem * ioaddr; /* Mapped address */ |