]> err.no Git - linux-2.6/commitdiff
[MMC] sdhci: proper timeout handling
authorPierre Ossman <drzeus@drzeus.cx>
Fri, 30 Jun 2006 09:22:25 +0000 (02:22 -0700)
committerRussell King <rmk+kernel@arm.linux.org.uk>
Sun, 2 Jul 2006 15:02:04 +0000 (16:02 +0100)
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>
drivers/mmc/sdhci.c
drivers/mmc/sdhci.h

index 77b7db27b55517d1929491d56dd70ef2896319d1..877226e2ffae6b68d81586e277b23bae3b537d8e 100644 (file)
@@ -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.
         */
index aed4abd37bfdd1c1b41fa77c218fbc1018e1ff1e..a8f45215ef3af833b41cc0c97f13a250c30eb779 100644 (file)
 /* 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 */