summaryrefslogtreecommitdiff
path: root/target/device/Atmel/arch-avr32/kernel-patches-2.6.22.10/linux-2.6.22.10-502-atmel_mci-fix-two-subtle-but-deadly-races.patch
blob: be99b83467f6273deaf6cdff318dded6eb37bad2 (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
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
From: Haavard Skinnemoen <hskinnemoen@atmel.com>
Subject: [PATCH 2/2] atmel_mci: Fix two subtle but deadly races

This patch fixes two possible races in the atmel_mci driver, at least
one of which may cause card probing to fail.

The first one may happen if a command fails and the next command is
queued before the controller is ready to accept a new one. Fix this by
not enabling error interrupts for commands and instead do any error
handling when the CMDRDY bit has been set.

The second one may happen after a successful read data transfer where
then next command is queued after the DMA transfer is complete, but
before the whole data transfer from the card is complete (i.e. the
card is still sending CRC, for example.) Fix this by waiting for the
NOTBUSY bit to be set before considering the request to be done. This
will also ensure that we actually see any CRC failures before
completing the request.

Signed-off-by: Haavard Skinnemoen <hskinnemoen@atmel.com>
---
 drivers/mmc/host/atmel-mci.c |  172 +++++++++++++-----------------------------
 1 files changed, 54 insertions(+), 118 deletions(-)

diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c
index 1dc91b4..45323c9 100644
--- a/drivers/mmc/host/atmel-mci.c
+++ b/drivers/mmc/host/atmel-mci.c
@@ -28,20 +28,15 @@
 
 #define DRIVER_NAME "atmel_mci"
 
-#define MCI_CMD_ERROR_FLAGS	(MCI_BIT(RINDE)	| MCI_BIT(RDIRE) |	\
-				 MCI_BIT(RCRCE) | MCI_BIT(RENDE) |	\
-				 MCI_BIT(RTOE))
 #define MCI_DATA_ERROR_FLAGS	(MCI_BIT(DCRCE) | MCI_BIT(DTOE) |	\
 				 MCI_BIT(OVRE) | MCI_BIT(UNRE))
 
 enum {
 	EVENT_CMD_COMPLETE = 0,
-	EVENT_CMD_ERROR,
 	EVENT_DATA_COMPLETE,
 	EVENT_DATA_ERROR,
 	EVENT_STOP_SENT,
 	EVENT_STOP_COMPLETE,
-	EVENT_STOP_ERROR,
 	EVENT_DMA_ERROR,
 	EVENT_CARD_DETECT,
 };
@@ -61,13 +56,14 @@ struct atmel_mci {
 	struct mmc_command	*cmd;
 	struct mmc_data		*data;
 
+	u32			cmd_status;
+	u32			data_status;
+	u32			stop_status;
 	u32			stop_cmdr;
-	u32			stop_iflags;
 
 	struct tasklet_struct	tasklet;
 	unsigned long		pending_events;
 	unsigned long		completed_events;
-	u32			error_status;
 
 	int			present;
 	int			detect_pin;
@@ -99,8 +95,6 @@ MODULE_PARM_DESC(fmax, "Max frequency in Hz of the MMC bus clock");
 /* Test bit macros for completed events */
 #define mci_cmd_is_complete(host)			\
 	test_bit(EVENT_CMD_COMPLETE, &host->completed_events)
-#define mci_cmd_error_is_complete(host)			\
-	test_bit(EVENT_CMD_ERROR, &host->completed_events)
 #define mci_data_is_complete(host)			\
 	test_bit(EVENT_DATA_COMPLETE, &host->completed_events)
 #define mci_data_error_is_complete(host)		\
@@ -109,8 +103,6 @@ MODULE_PARM_DESC(fmax, "Max frequency in Hz of the MMC bus clock");
 	test_bit(EVENT_STOP_SENT, &host->completed_events)
 #define mci_stop_is_complete(host)			\
 	test_bit(EVENT_STOP_COMPLETE, &host->completed_events)
-#define mci_stop_error_is_complete(host)		\
-	test_bit(EVENT_STOP_ERROR, &host->completed_events)
 #define mci_dma_error_is_complete(host)			\
 	test_bit(EVENT_DMA_ERROR, &host->completed_events)
 #define mci_card_detect_is_complete(host)			\
@@ -119,8 +111,6 @@ MODULE_PARM_DESC(fmax, "Max frequency in Hz of the MMC bus clock");
 /* Test and clear bit macros for pending events */
 #define mci_clear_cmd_is_pending(host)			\
 	test_and_clear_bit(EVENT_CMD_COMPLETE, &host->pending_events)
-#define mci_clear_cmd_error_is_pending(host)		\
-	test_and_clear_bit(EVENT_CMD_ERROR, &host->pending_events)
 #define mci_clear_data_is_pending(host)			\
 	test_and_clear_bit(EVENT_DATA_COMPLETE, &host->pending_events)
 #define mci_clear_data_error_is_pending(host)		\
@@ -129,8 +119,6 @@ MODULE_PARM_DESC(fmax, "Max frequency in Hz of the MMC bus clock");
 	test_and_clear_bit(EVENT_STOP_SENT, &host->pending_events)
 #define mci_clear_stop_is_pending(host)			\
 	test_and_clear_bit(EVENT_STOP_COMPLETE, &host->pending_events)
-#define mci_clear_stop_error_is_pending(host)		\
-	test_and_clear_bit(EVENT_STOP_ERROR, &host->pending_events)
 #define mci_clear_dma_error_is_pending(host)		\
 	test_and_clear_bit(EVENT_DMA_ERROR, &host->pending_events)
 #define mci_clear_card_detect_is_pending(host)		\
@@ -139,8 +127,6 @@ MODULE_PARM_DESC(fmax, "Max frequency in Hz of the MMC bus clock");
 /* Test and set bit macros for completed events */
 #define mci_set_cmd_is_completed(host)			\
 	test_and_set_bit(EVENT_CMD_COMPLETE, &host->completed_events)
-#define mci_set_cmd_error_is_completed(host)		\
-	test_and_set_bit(EVENT_CMD_ERROR, &host->completed_events)
 #define mci_set_data_is_completed(host)			\
 	test_and_set_bit(EVENT_DATA_COMPLETE, &host->completed_events)
 #define mci_set_data_error_is_completed(host)		\
@@ -149,8 +135,6 @@ MODULE_PARM_DESC(fmax, "Max frequency in Hz of the MMC bus clock");
 	test_and_set_bit(EVENT_STOP_SENT, &host->completed_events)
 #define mci_set_stop_is_completed(host)			\
 	test_and_set_bit(EVENT_STOP_COMPLETE, &host->completed_events)
-#define mci_set_stop_error_is_completed(host)		\
-	test_and_set_bit(EVENT_STOP_ERROR, &host->completed_events)
 #define mci_set_dma_error_is_completed(host)		\
 	test_and_set_bit(EVENT_DMA_ERROR, &host->completed_events)
 #define mci_set_card_detect_is_completed(host)		\
@@ -159,8 +143,6 @@ MODULE_PARM_DESC(fmax, "Max frequency in Hz of the MMC bus clock");
 /* Set bit macros for completed events */
 #define mci_set_cmd_complete(host)			\
 	set_bit(EVENT_CMD_COMPLETE, &host->completed_events)
-#define mci_set_cmd_error_complete(host)		\
-	set_bit(EVENT_CMD_ERROR, &host->completed_events)
 #define mci_set_data_complete(host)			\
 	set_bit(EVENT_DATA_COMPLETE, &host->completed_events)
 #define mci_set_data_error_complete(host)		\
@@ -169,8 +151,6 @@ MODULE_PARM_DESC(fmax, "Max frequency in Hz of the MMC bus clock");
 	set_bit(EVENT_STOP_SENT, &host->completed_events)
 #define mci_set_stop_complete(host)			\
 	set_bit(EVENT_STOP_COMPLETE, &host->completed_events)
-#define mci_set_stop_error_complete(host)		\
-	set_bit(EVENT_STOP_ERROR, &host->completed_events)
 #define mci_set_dma_error_complete(host)		\
 	set_bit(EVENT_DMA_ERROR, &host->completed_events)
 #define mci_set_card_detect_complete(host)		\
@@ -179,8 +159,6 @@ MODULE_PARM_DESC(fmax, "Max frequency in Hz of the MMC bus clock");
 /* Set bit macros for pending events */
 #define mci_set_cmd_pending(host)			\
 	set_bit(EVENT_CMD_COMPLETE, &host->pending_events)
-#define mci_set_cmd_error_pending(host)			\
-	set_bit(EVENT_CMD_ERROR, &host->pending_events)
 #define mci_set_data_pending(host)			\
 	set_bit(EVENT_DATA_COMPLETE, &host->pending_events)
 #define mci_set_data_error_pending(host)		\
@@ -189,8 +167,6 @@ MODULE_PARM_DESC(fmax, "Max frequency in Hz of the MMC bus clock");
 	set_bit(EVENT_STOP_SENT, &host->pending_events)
 #define mci_set_stop_pending(host)			\
 	set_bit(EVENT_STOP_COMPLETE, &host->pending_events)
-#define mci_set_stop_error_pending(host)		\
-	set_bit(EVENT_STOP_ERROR, &host->pending_events)
 #define mci_set_dma_error_pending(host)			\
 	set_bit(EVENT_DMA_ERROR, &host->pending_events)
 #define mci_set_card_detect_pending(host)		\
@@ -199,8 +175,6 @@ MODULE_PARM_DESC(fmax, "Max frequency in Hz of the MMC bus clock");
 /* Clear bit macros for pending events */
 #define mci_clear_cmd_pending(host)			\
 	clear_bit(EVENT_CMD_COMPLETE, &host->pending_events)
-#define mci_clear_cmd_error_pending(host)		\
-	clear_bit(EVENT_CMD_ERROR, &host->pending_events)
 #define mci_clear_data_pending(host)			\
 	clear_bit(EVENT_DATA_COMPLETE, &host->pending_events)
 #define mci_clear_data_error_pending(host)		\
@@ -209,8 +183,6 @@ MODULE_PARM_DESC(fmax, "Max frequency in Hz of the MMC bus clock");
 	clear_bit(EVENT_STOP_SENT, &host->pending_events)
 #define mci_clear_stop_pending(host)			\
 	clear_bit(EVENT_STOP_COMPLETE, &host->pending_events)
-#define mci_clear_stop_error_pending(host)		\
-	clear_bit(EVENT_STOP_ERROR, &host->pending_events)
 #define mci_clear_dma_error_pending(host)		\
 	clear_bit(EVENT_DMA_ERROR, &host->pending_events)
 #define mci_clear_card_detect_pending(host)		\
@@ -471,20 +443,16 @@ static void atmci_set_timeout(struct atmel_mci *host,
 }
 
 /*
- * Return mask with interrupt flags to be handled for this command.
+ * Return mask with command flags to be enabled for this command.
  */
 static u32 atmci_prepare_command(struct mmc_host *mmc,
-				 struct mmc_command *cmd,
-				 u32 *cmd_flags)
+				 struct mmc_command *cmd)
 {
 	u32 cmdr;
-	u32 iflags;
 
 	cmd->error = MMC_ERR_NONE;
 
-	cmdr = 0;
-	BUG_ON(MCI_BFEXT(CMDNB, cmdr) != 0);
-	cmdr = MCI_BFINS(CMDNB, cmd->opcode, cmdr);
+	cmdr = MCI_BF(CMDNB, cmd->opcode);
 
 	if (cmd->flags & MMC_RSP_PRESENT) {
 		if (cmd->flags & MMC_RSP_136)
@@ -503,16 +471,11 @@ static u32 atmci_prepare_command(struct mmc_host *mmc,
 	if (mmc->ios.bus_mode == MMC_BUSMODE_OPENDRAIN)
 		cmdr |= MCI_BIT(OPDCMD);
 
-	iflags = MCI_BIT(CMDRDY) | MCI_CMD_ERROR_FLAGS;
-	if (!(cmd->flags & MMC_RSP_CRC))
-		iflags &= ~MCI_BIT(RCRCE);
-
 	dev_dbg(&mmc->class_dev,
 		"cmd: op %02x arg %08x flags %08x, cmdflags %08lx\n",
 		cmd->opcode, cmd->arg, cmd->flags, (unsigned long)cmdr);
 
-	*cmd_flags = cmdr;
-	return iflags;
+	return cmdr;
 }
 
 static void atmci_start_command(struct atmel_mci *host,
@@ -596,13 +559,13 @@ static void atmci_request(struct mmc_host *mmc, struct mmc_request *mrq)
 	host->pending_events = 0;
 	host->completed_events = 0;
 
-	iflags = atmci_prepare_command(mmc, mrq->cmd, &cmdflags);
+	iflags = MCI_BIT(CMDRDY);
+	cmdflags = atmci_prepare_command(mmc, mrq->cmd);
 
 	if (mrq->stop) {
-		BUG_ON(!data);
+		WARN_ON(!data);
 
-		host->stop_iflags = atmci_prepare_command(mmc, mrq->stop,
-							  &host->stop_cmdr);
+		host->stop_cmdr = atmci_prepare_command(mmc, mrq->stop);
 		host->stop_cmdr |= MCI_BF(TRCMD, MCI_TRCMD_STOP_TRANS);
 		if (!(data->flags & MMC_DATA_WRITE))
 			host->stop_cmdr |= MCI_BIT(TRDIR);
@@ -716,7 +679,7 @@ static void send_stop_cmd(struct mmc_host *mmc, struct mmc_data *data,
 	struct atmel_mci *host = mmc_priv(mmc);
 
 	atmci_start_command(host, data->stop, host->stop_cmdr | flags);
-	mci_writel(host, IER, host->stop_iflags);
+	mci_writel(host, IER, MCI_BIT(CMDRDY));
 }
 
 static void atmci_data_complete(struct atmel_mci *host, struct mmc_data *data)
@@ -735,18 +698,30 @@ static void atmci_data_complete(struct atmel_mci *host, struct mmc_data *data)
 		atmci_request_end(host->mmc, data->mrq);
 }
 
-static void atmci_command_error(struct mmc_host *mmc,
-				struct mmc_command *cmd,
-				u32 status)
+static void atmci_command_complete(struct atmel_mci *host,
+			struct mmc_command *cmd, u32 status)
 {
-	dev_dbg(&mmc->class_dev, "command error: status=0x%08x\n", status);
-
 	if (status & MCI_BIT(RTOE))
 		cmd->error = MMC_ERR_TIMEOUT;
-	else if (status & MCI_BIT(RCRCE))
+	else if ((cmd->flags & MMC_RSP_CRC)
+			&& (status & MCI_BIT(RCRCE)))
 		cmd->error = MMC_ERR_BADCRC;
-	else
+	else if (status & (MCI_BIT(RINDE) | MCI_BIT(RDIRE) | MCI_BIT(RENDE)))
 		cmd->error = MMC_ERR_FAILED;
+
+	if (cmd->error != MMC_ERR_NONE) {
+		dev_dbg(&host->mmc->class_dev,
+				"command error: op=0x%x status=0x%08x\n",
+				cmd->opcode, status);
+
+		if (cmd->data) {
+			dma_stop_request(host->dma.req.req.dmac,
+					host->dma.req.req.channel);
+			mci_writel(host, IDR, MCI_BIT(NOTBUSY)
+					| MCI_DATA_ERROR_FLAGS);
+			host->data = NULL;
+		}
+	}
 }
 
 static void atmci_tasklet_func(unsigned long priv)
@@ -761,38 +736,16 @@ static void atmci_tasklet_func(unsigned long priv)
 		 host->pending_events, host->completed_events,
 		 mci_readl(host, IMR));
 
-	if (mci_clear_cmd_error_is_pending(host)) {
-		struct mmc_command *cmd;
-
-		mci_set_cmd_error_complete(host);
-		mci_clear_cmd_pending(host);
-		cmd = host->mrq->cmd;
-
-		if (cmd->data) {
-			dma_stop_request(host->dma.req.req.dmac,
-					 host->dma.req.req.channel);
-			host->data = NULL;
-		}
-
-		atmci_command_error(mmc, cmd, host->error_status);
-		atmci_request_end(mmc, cmd->mrq);
-	}
-	if (mci_clear_stop_error_is_pending(host)) {
-		mci_set_stop_error_complete(host);
-		mci_clear_stop_pending(host);
-		atmci_command_error(mmc, host->mrq->stop,
-				    host->error_status);
-		if (!host->data)
-			atmci_request_end(mmc, host->mrq);
-	}
 	if (mci_clear_cmd_is_pending(host)) {
 		mci_set_cmd_complete(host);
-		if (!mrq->data || mci_data_is_complete(host)
+		atmci_command_complete(host, mrq->cmd, host->cmd_status);
+		if (!host->data || mci_data_is_complete(host)
 		    || mci_data_error_is_complete(host))
 			atmci_request_end(mmc, mrq);
 	}
 	if (mci_clear_stop_is_pending(host)) {
 		mci_set_stop_complete(host);
+		atmci_command_complete(host, mrq->stop, host->stop_status);
 		if (mci_data_is_complete(host)
 		    || mci_data_error_is_complete(host))
 			atmci_request_end(mmc, mrq);
@@ -815,7 +768,7 @@ static void atmci_tasklet_func(unsigned long priv)
 		atmci_data_complete(host, data);
 	}
 	if (mci_clear_data_error_is_pending(host)) {
-		u32 status = host->error_status;
+		u32 status = host->data_status;
 
 		mci_set_data_error_complete(host);
 		mci_clear_data_pending(host);
@@ -858,10 +811,8 @@ static void atmci_tasklet_func(unsigned long priv)
 
 		/* Clean up queue if present */
 		if (mrq) {
-			if (!mci_cmd_is_complete(host)
-			    && !mci_cmd_error_is_complete(host)) {
+			if (!mci_cmd_is_complete(host))
 				mrq->cmd->error = MMC_ERR_TIMEOUT;
-			}
 			if (mrq->data && !mci_data_is_complete(host)
 			    && !mci_data_error_is_complete(host)) {
 				dma_stop_request(host->dma.req.req.dmac,
@@ -869,10 +820,8 @@ static void atmci_tasklet_func(unsigned long priv)
 				host->data->error = MMC_ERR_TIMEOUT;
 				atmci_data_complete(host, data);
 			}
-			if (mrq->stop && !mci_stop_is_complete(host)
-			    && !mci_stop_error_is_complete(host)) {
+			if (mrq->stop && !mci_stop_is_complete(host))
 				mrq->stop->error = MMC_ERR_TIMEOUT;
-			}
 
 			host->cmd = NULL;
 			atmci_request_end(mmc, mrq);
@@ -895,13 +844,16 @@ static void atmci_cmd_interrupt(struct mmc_host *mmc, u32 status)
 	cmd->resp[2] = mci_readl(host, RSPR);
 	cmd->resp[3] = mci_readl(host, RSPR);
 
-	mci_writel(host, IDR, MCI_BIT(CMDRDY) | MCI_CMD_ERROR_FLAGS);
+	mci_writel(host, IDR, MCI_BIT(CMDRDY));
 	host->cmd = NULL;
 
-	if (mci_stop_sent_is_complete(host))
+	if (mci_stop_sent_is_complete(host)) {
+		host->stop_status = status;
 		mci_set_stop_pending(host);
-	else
+	} else {
+		host->cmd_status = status;
 		mci_set_cmd_pending(host);
+	}
 
 	tasklet_schedule(&host->tasklet);
 }
@@ -920,18 +872,16 @@ static void atmci_xfer_complete(struct dma_request *_req)
 	if (data->stop && !mci_set_stop_sent_is_completed(host))
 		send_stop_cmd(host->mmc, data, 0);
 
-	if (data->flags & MMC_DATA_READ) {
-		mci_writel(host, IDR, MCI_DATA_ERROR_FLAGS);
-		mci_set_data_pending(host);
-		tasklet_schedule(&host->tasklet);
-	} else {
-		/*
-		 * For the WRITE case, wait for NOTBUSY. This function
-		 * is called when everything has been written to the
-		 * controller, not when the card is done programming.
-		 */
-		mci_writel(host, IER, MCI_BIT(NOTBUSY));
-	}
+	/*
+	 * Regardless of what the documentation says, we have to wait
+	 * for NOTBUSY even after block read operations.
+	 *
+	 * When the DMA transfer is complete, the controller may still
+	 * be reading the CRC from the card, i.e. the data transfer is
+	 * still in progress and we haven't seen all the potential
+	 * error bits yet.
+	 */
+	mci_writel(host, IER, MCI_BIT(NOTBUSY));
 }
 
 static void atmci_dma_error(struct dma_request *_req)
@@ -963,24 +913,10 @@ static irqreturn_t atmci_interrupt(int irq, void *dev_id)
 	pending = status & mask;
 
 	do {
-		if (pending & MCI_CMD_ERROR_FLAGS) {
-			mci_writel(host, IDR, (MCI_BIT(CMDRDY)
-					       | MCI_BIT(NOTBUSY)
-					       | MCI_CMD_ERROR_FLAGS
-					       | MCI_DATA_ERROR_FLAGS));
-			host->error_status = status;
-			host->cmd = NULL;
-			if (mci_stop_sent_is_complete(host))
-				mci_set_stop_error_pending(host);
-			else
-				mci_set_cmd_error_pending(host);
-			tasklet_schedule(&host->tasklet);
-			break;
-		}
 		if (pending & MCI_DATA_ERROR_FLAGS) {
 			mci_writel(host, IDR, (MCI_BIT(NOTBUSY)
 					       | MCI_DATA_ERROR_FLAGS));
-			host->error_status = status;
+			host->data_status = status;
 			mci_set_data_error_pending(host);
 			tasklet_schedule(&host->tasklet);
 			break;
-- 
1.5.3.2

_______________________________________________
Kernel mailing list
Kernel@avr32linux.org
http://duppen.flaskehals.net/cgi-bin/mailman/listinfo/kernel