diff options
author | Pierre Ossman <drzeus@drzeus.cx> | 2006-06-30 02:22:25 -0700 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2006-07-02 16:02:04 +0100 |
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 77b7db27b55..877226e2ffa 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -128,9 +128,6 @@ static void sdhci_init(struct sdhci_host *host) writel(intmask, host->ioaddr + SDHCI_INT_ENABLE); writel(intmask, host->ioaddr + SDHCI_SIGNAL_ENABLE); - - /* This is unknown magic. */ - writeb(0xE, host->ioaddr + SDHCI_TIMEOUT_CONTROL); } static void sdhci_activate_led(struct sdhci_host *host) @@ -274,6 +271,8 @@ static void sdhci_transfer_pio(struct sdhci_host *host) static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data) { u16 mode; + u8 count; + unsigned target_timeout, current_timeout; WARN_ON(host->data); @@ -287,6 +286,37 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data) DBG("tsac %d ms nsac %d clk\n", data->timeout_ns / 1000000, data->timeout_clks); + /* timeout in us */ + target_timeout = data->timeout_ns / 1000 + + data->timeout_clks / host->clock; + + /* + * Figure out needed cycles. + * We do this in steps in order to fit inside a 32 bit int. + * The first step is the minimum timeout, which will have a + * minimum resolution of 6 bits: + * (1) 2^13*1000 > 2^22, + * (2) host->timeout_clk < 2^16 + * => + * (1) / (2) > 2^6 + */ + count = 0; + current_timeout = (1 << 13) * 1000 / host->timeout_clk; + while (current_timeout < target_timeout) { + count++; + current_timeout <<= 1; + if (count >= 0xF) + break; + } + + if (count >= 0xF) { + printk(KERN_WARNING "%s: Too large timeout requested!\n", + mmc_hostname(host->mmc)); + count = 0xE; + } + + writeb(count, host->ioaddr + SDHCI_TIMEOUT_CONTROL); + mode = SDHCI_TRNS_BLK_CNT_EN; if (data->blocks > 1) mode |= SDHCI_TRNS_MULTI; @@ -1096,6 +1126,17 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot) } host->max_clk *= 1000000; + 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", host->slot_descr); + ret = -ENODEV; + goto unmap; + } + if (caps & SDHCI_TIMEOUT_CLK_UNIT) + host->timeout_clk *= 1000; + /* * Set host parameters. */ diff --git a/drivers/mmc/sdhci.h b/drivers/mmc/sdhci.h index aed4abd37bf..a8f45215ef3 100644 --- a/drivers/mmc/sdhci.h +++ b/drivers/mmc/sdhci.h @@ -125,6 +125,9 @@ /* 3E-3F reserved */ #define SDHCI_CAPABILITIES 0x40 +#define SDHCI_TIMEOUT_CLK_MASK 0x0000003F +#define SDHCI_TIMEOUT_CLK_SHIFT 0 +#define SDHCI_TIMEOUT_CLK_UNIT 0x00000080 #define SDHCI_CLOCK_BASE_MASK 0x00003F00 #define SDHCI_CLOCK_BASE_SHIFT 8 #define SDHCI_CAN_DO_DMA 0x00400000 @@ -156,6 +159,7 @@ struct sdhci_host { #define SDHCI_USE_DMA (1<<0) unsigned int max_clk; /* Max possible freq (MHz) */ + unsigned int timeout_clk; /* Timeout freq (KHz) */ unsigned int clock; /* Current clock (MHz) */ unsigned short power; /* Current voltage */ |