[PATCH 06/21] [MMC] Proper timeout handling

From: Pierre Ossman
Date: Wed Jun 21 2006 - 10:26:14 EST


Use the give timeout clock and calculate a proper timeout instead of using
the maximum at all times.

Signed-off-by: Pierre Ossman <drzeus@xxxxxxxxx>
---

drivers/mmc/sdhci.c | 47 ++++++++++++++++++++++++++++++++++++++++++++---
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 d904435..feadc2d 100644
--- a/drivers/mmc/sdhci.c
+++ b/drivers/mmc/sdhci.c
@@ -128,9 +128,6 @@ static void sdhci_init(struct sdhci_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 sd
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 sd
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(st
}
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 aed4abd..a8f4521 100644
--- a/drivers/mmc/sdhci.h
+++ b/drivers/mmc/sdhci.h
@@ -125,6 +125,9 @@ #define SDHCI_ACMD12_ERR 0x3C
/* 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 */

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/