summaryrefslogtreecommitdiffstats
path: root/patches/linux-3.6.10/0018-Fix-for-mmc-timeouts-when-erasing-multiple-blocks.patch
blob: c87441b25857ccd0e02329c6a7b9dc9d66ae2dc1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
From: popcornmix <popcornmix@gmail.com>
Date: Sat, 17 Mar 2012 23:28:22 +0000
Subject: [PATCH] Fix for mmc timeouts when erasing multiple blocks

---
 arch/arm/mach-bcm2708/power.c    | 10 +++--
 drivers/mmc/host/sdhci-bcm2708.c | 97 +++++++++++++++++++++++++++++++++++++---
 2 files changed, 97 insertions(+), 10 deletions(-)

diff --git a/arch/arm/mach-bcm2708/power.c b/arch/arm/mach-bcm2708/power.c
index 10aef47..a4139fc 100644
--- a/arch/arm/mach-bcm2708/power.c
+++ b/arch/arm/mach-bcm2708/power.c
@@ -14,6 +14,7 @@
 #include <linux/module.h>
 #include <linux/semaphore.h>
 #include <linux/bug.h>
+#include <linux/delay.h>
 #include <mach/power.h>
 #include <mach/vcio.h>
 #include <mach/arm_power.h>
@@ -97,6 +98,7 @@ int bcm_power_request(BCM_POWER_HANDLE_T handle, uint32_t request)
 						  global_request << 4);
 
 				/* Wait for a response during power-up */
+				mdelay(10);
 				if (global_request & ~g_state.global_request) {
 					rc = bcm_mailbox_read(MBOX_CHAN_POWER,
 							      &actual);
@@ -111,14 +113,14 @@ int bcm_power_request(BCM_POWER_HANDLE_T handle, uint32_t request)
 
 				if (rc == 0) {
 					if (actual != global_request) {
-						printk(KERN_ERR
-						     "%s: prev global %x, new global %x, actual %x, request %x, others_request %x\n",
+						printk(KERN_INFO
+						     "%s: Fail: prev global %x, new global %x, actual %x request %x, others_request %x\n",
 						     __func__,
 						     g_state.global_request,
 						     global_request, actual, request, others_request);
 						/* A failure */
-						BUG_ON((others_request & actual)
-						       != others_request);
+					//	BUG_ON((others_request & actual)
+					//	       != others_request);
 						request &= actual;
 						rc = -EIO;
 					}
diff --git a/drivers/mmc/host/sdhci-bcm2708.c b/drivers/mmc/host/sdhci-bcm2708.c
index 2dd6bed..398a04e 100644
--- a/drivers/mmc/host/sdhci-bcm2708.c
+++ b/drivers/mmc/host/sdhci-bcm2708.c
@@ -26,7 +26,9 @@
 #include <linux/highmem.h>
 #include <linux/platform_device.h>
 #include <linux/module.h>
+#include <linux/mmc/mmc.h>
 #include <linux/mmc/host.h>
+#include <linux/mmc/sd.h>
 
 #include <linux/io.h>
 #include <linux/dma-mapping.h>
@@ -68,6 +70,9 @@
 
 #define BCM2708_SDHCI_SLEEP_TIMEOUT 1000   /* msecs */
 
+/* Mhz clock that the EMMC core is running at. Should match the platform clockman settings */
+#define BCM2708_EMMC_CLOCK_FREQ 80000000
+
 #define POWER_OFF 0
 #define POWER_LAZY_OFF 1
 #define POWER_ON  2
@@ -222,6 +227,10 @@ u8 sdhci_bcm2708_readb(struct sdhci_host *host, int reg)
 
 static void sdhci_bcm2708_raw_writel(struct sdhci_host *host, u32 val, int reg)
 {
+	u32 ier;
+	static bool timeout_disabled = false;
+	unsigned int ns_2clk = 0;
+
 	/* The Arasan has a bugette whereby it may lose the content of
 	 * successive writes to registers that are within two SD-card clock
 	 * cycles of each other (a clock domain crossing problem).
@@ -234,7 +243,7 @@ static void sdhci_bcm2708_raw_writel(struct sdhci_host *host, u32 val, int reg)
 		/* host->clock is the clock freq in Hz */
 		static hptime_t last_write_hpt;
 		hptime_t now = hptime();
-		unsigned int ns_2clk = 2000000000/host->clock;
+		ns_2clk = 2000000000/host->clock;
 
 		if (now == last_write_hpt || now == last_write_hpt+1) {
 			 /* we can't guarantee any significant time has
@@ -250,6 +259,24 @@ static void sdhci_bcm2708_raw_writel(struct sdhci_host *host, u32 val, int reg)
 		}
 		last_write_hpt = now;
 	}
+#if 1
+	/* The Arasan is clocked for timeouts using the SD clock which is too fast
+	 * for ERASE commands and causes issues. So we disable timeouts for ERASE */
+	if (host->cmd != NULL && host->cmd->opcode == MMC_ERASE && reg == (SDHCI_COMMAND & ~3)) {
+		mod_timer(&host->timer, jiffies + 30 * HZ);
+		ier = readl(host->ioaddr + SDHCI_SIGNAL_ENABLE);
+		ier &= ~SDHCI_INT_DATA_TIMEOUT;
+		writel(ier, host->ioaddr + SDHCI_SIGNAL_ENABLE);
+		timeout_disabled = true;
+		udelay((ns_2clk+1000-1)/1000);
+	} else if (timeout_disabled) {
+		ier = readl(host->ioaddr + SDHCI_SIGNAL_ENABLE);
+		ier |= SDHCI_INT_DATA_TIMEOUT;
+		writel(ier, host->ioaddr + SDHCI_SIGNAL_ENABLE);
+		timeout_disabled = false;
+		udelay((ns_2clk+1000-1)/1000);
+	}
+#endif
 	writel(val, host->ioaddr + reg);
 #else
 	void __iomem * regaddr = host->ioaddr + reg;
@@ -325,14 +352,68 @@ void sdhci_bcm2708_writeb(struct sdhci_host *host, u8 val, int reg)
 
 static unsigned int sdhci_bcm2708_get_max_clock(struct sdhci_host *host)
 {
-	return 100000000;	// this value is in Hz (100MHz/4)
+	return 20000000;	// this value is in Hz (20MHz)
 }
 
 static unsigned int sdhci_bcm2708_get_timeout_clock(struct sdhci_host *host)
 {
-	return 100000;		// this value is in kHz (100MHz/4)
+	if(host->clock)
+		return (host->clock / 1000);		// this value is in kHz (100MHz)
+	else
+		return (sdhci_bcm2708_get_max_clock(host) / 1000);
 }
 
+static void sdhci_bcm2708_set_clock(struct sdhci_host *host, unsigned int clock)
+{
+	int div = 0;
+	u16 clk = 0;
+	unsigned long timeout;
+
+        if (clock == host->clock)
+                return;
+
+        sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL);
+
+        if (clock == 0)
+                goto out;
+
+	if (BCM2708_EMMC_CLOCK_FREQ <= clock)
+		div = 1;
+	else {
+		for (div = 2; div < SDHCI_MAX_DIV_SPEC_300; div += 2) {
+			if ((BCM2708_EMMC_CLOCK_FREQ / div) <= clock)
+				break;
+		}
+	}
+
+        DBG( "desired SD clock: %d, actual: %d\n",
+                clock, BCM2708_EMMC_CLOCK_FREQ / div);
+
+	clk |= (div & SDHCI_DIV_MASK) << SDHCI_DIVIDER_SHIFT;
+	clk |= ((div & SDHCI_DIV_HI_MASK) >> SDHCI_DIV_MASK_LEN)
+		<< SDHCI_DIVIDER_HI_SHIFT;
+	clk |= SDHCI_CLOCK_INT_EN;
+
+	sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
+
+        timeout = 20;
+        while (!((clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL))
+                        & SDHCI_CLOCK_INT_STABLE)) {
+                if (timeout == 0) {
+			printk(KERN_ERR "%s: Internal clock never "
+				"stabilised.\n", mmc_hostname(host->mmc));
+                        return;
+                }
+                timeout--;
+                mdelay(1);
+        }
+
+        clk |= SDHCI_CLOCK_CARD_EN;
+        sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
+out:
+        host->clock = clock;
+ }
+
 /*****************************************************************************\
  *									     *
  * DMA Operation							     *
@@ -821,7 +902,7 @@ static void sdhci_bcm2708_dma_complete_irq(struct sdhci_host *host,
 			continue;
 
 		if (1000000-timeout > 4000) /*ave. is about 3250*/
-			printk(KERN_INFO "%s: note - long %s sync %luns - "
+			DBG("%s: note - long %s sync %luns - "
 			       "%d its.\n",
 			       mmc_hostname(host->mmc),
 			       data->flags & MMC_DATA_READ? "read": "write",
@@ -1219,7 +1300,7 @@ static struct sdhci_ops sdhci_bcm2708_ops = {
 #error The BCM2708 SDHCI driver needs CONFIG_MMC_SDHCI_IO_ACCESSORS to be set
 #endif
 	//.enable_dma = NULL,
-	//.set_clock = NULL,
+	.set_clock = sdhci_bcm2708_set_clock,
 	.get_max_clock = sdhci_bcm2708_get_max_clock,
 	//.get_min_clock = NULL,
 	.get_timeout_clock = sdhci_bcm2708_get_timeout_clock,
@@ -1282,7 +1363,9 @@ static int __devinit sdhci_bcm2708_probe(struct platform_device *pdev)
 	host->irq = platform_get_irq(pdev, 0);
 
 	host->quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION |
-		       SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK;
+		       SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
+		       SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
+		       SDHCI_QUIRK_NONSTANDARD_CLOCK;
 #ifdef CONFIG_MMC_SDHCI_BCM2708_DMA
 	host->flags = SDHCI_USE_PLATDMA;
 #endif
@@ -1349,6 +1432,8 @@ static int __devinit sdhci_bcm2708_probe(struct platform_device *pdev)
 	    host_priv->cb_base, (unsigned)host_priv->cb_handle,
 	    host_priv->dma_chan, host_priv->dma_chan_base,
 	    host_priv->dma_irq);
+
+	host->mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED;
 #endif
 
 	ret = sdhci_add_host(host);