浏览代码

[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>
Pierre Ossman 19 年之前
父节点
当前提交
1c8cde92fa
共有 2 个文件被更改,包括 48 次插入3 次删除
  1. 44 3
      drivers/mmc/sdhci.c
  2. 4 0
      drivers/mmc/sdhci.h

+ 44 - 3
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.
 	 */

+ 4 - 0
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 */