aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlex Dubov <oakad@yahoo.com>2007-04-12 03:05:23 -0400
committerPierre Ossman <drzeus@drzeus.cx>2007-05-01 07:04:14 -0400
commit72dc9d9619dd4682f4197e7a7f19af22fd6516a7 (patch)
treefc761d3189979ddfec88848fb744d8275cf812c3
parentdfef26d9aad4f983da232b259ee7f7faec479b2d (diff)
tifm_sd: replace command completion state machine with full checking
State machine used to to track mmc command state was found to be fragile and unreliable, making many cards unusable. The safer solution is to perform all needed checks at every card event. Signed-off-by: Alex Dubov <oakad@yahoo.com> Signed-off-by: Pierre Ossman <drzeus@drzeus.cx>
-rw-r--r--drivers/mmc/tifm_sd.c259
-rw-r--r--include/linux/tifm.h1
2 files changed, 138 insertions, 122 deletions
diff --git a/drivers/mmc/tifm_sd.c b/drivers/mmc/tifm_sd.c
index 52499548abe8..103060f490a6 100644
--- a/drivers/mmc/tifm_sd.c
+++ b/drivers/mmc/tifm_sd.c
@@ -70,19 +70,14 @@ module_param(fixed_timeout, bool, 0644);
70#define TIFM_MMCSD_CMD_AC 0x2000 70#define TIFM_MMCSD_CMD_AC 0x2000
71#define TIFM_MMCSD_CMD_ADTC 0x3000 71#define TIFM_MMCSD_CMD_ADTC 0x3000
72 72
73typedef enum {
74 IDLE = 0,
75 CMD, /* main command ended */
76 BRS, /* block transfer finished */
77 SCMD, /* stop command ended */
78 CARD, /* card left busy state */
79 FIFO, /* FIFO operation completed (uncertain) */
80 READY
81} card_state_t;
82
83enum { 73enum {
84 FIFO_RDY = 0x0001, /* hardware dependent value */ 74 CMD_READY = 0x0001,
85 CARD_BUSY = 0x0010 75 FIFO_READY = 0x0002,
76 BRS_READY = 0x0004,
77 SCMD_ACTIVE = 0x0008,
78 SCMD_READY = 0x0010,
79 CARD_BUSY = 0x0020,
80 DATA_CARRY = 0x0040
86}; 81};
87 82
88struct tifm_sd { 83struct tifm_sd {
@@ -92,7 +87,7 @@ struct tifm_sd {
92 open_drain:1, 87 open_drain:1,
93 no_dma:1; 88 no_dma:1;
94 unsigned short cmd_flags; 89 unsigned short cmd_flags;
95 card_state_t state; 90
96 unsigned int clk_freq; 91 unsigned int clk_freq;
97 unsigned int clk_div; 92 unsigned int clk_div;
98 unsigned long timeout_jiffies; 93 unsigned long timeout_jiffies;
@@ -234,87 +229,76 @@ static void tifm_sd_fetch_resp(struct mmc_command *cmd, struct tifm_dev *sock)
234 | readl(sock->addr + SOCK_MMCSD_RESPONSE + 0x00); 229 | readl(sock->addr + SOCK_MMCSD_RESPONSE + 0x00);
235} 230}
236 231
237static void tifm_sd_process_cmd(struct tifm_dev *sock, struct tifm_sd *host, 232static void tifm_sd_check_status(struct tifm_sd *host)
238 unsigned int host_status)
239{ 233{
234 struct tifm_dev *sock = host->dev;
240 struct mmc_command *cmd = host->req->cmd; 235 struct mmc_command *cmd = host->req->cmd;
241 236
242change_state: 237 if (cmd->error != MMC_ERR_NONE)
243 switch (host->state) { 238 goto finish_request;
244 case IDLE: 239
240 if (!(host->cmd_flags & CMD_READY))
245 return; 241 return;
246 case CMD: 242
247 if (host_status & (TIFM_MMCSD_EOC | TIFM_MMCSD_CERR)) { 243 if (cmd->data) {
248 tifm_sd_fetch_resp(cmd, sock); 244 if (cmd->data->error != MMC_ERR_NONE) {
249 if (cmd->data) { 245 if ((host->cmd_flags & SCMD_ACTIVE)
250 host->state = BRS; 246 && !(host->cmd_flags & SCMD_READY))
251 } else { 247 return;
252 host->state = READY; 248
253 } 249 goto finish_request;
254 goto change_state;
255 }
256 break;
257 case BRS:
258 if (tifm_sd_transfer_data(sock, host, host_status)) {
259 if (cmd->data->flags & MMC_DATA_WRITE) {
260 host->state = CARD;
261 } else {
262 if (host->no_dma) {
263 if (host->req->stop) {
264 tifm_sd_exec(host, host->req->stop);
265 host->state = SCMD;
266 } else {
267 host->state = READY;
268 }
269 } else {
270 host->state = FIFO;
271 }
272 }
273 goto change_state;
274 }
275 break;
276 case SCMD:
277 if (host_status & TIFM_MMCSD_EOC) {
278 tifm_sd_fetch_resp(host->req->stop, sock);
279 host->state = READY;
280 goto change_state;
281 } 250 }
282 break; 251
283 case CARD: 252 if (!(host->cmd_flags & BRS_READY))
284 dev_dbg(&sock->dev, "waiting for CARD, have %zd blocks\n", 253 return;
285 host->written_blocks); 254
286 if (!(host->cmd_flags & CARD_BUSY) 255 if (!(host->no_dma || (host->cmd_flags & FIFO_READY)))
287 && (host->written_blocks == cmd->data->blocks)) { 256 return;
288 if (host->no_dma) { 257
289 if (host->req->stop) { 258 if (cmd->data->flags & MMC_DATA_WRITE) {
259 if (host->req->stop) {
260 if (!(host->cmd_flags & SCMD_ACTIVE)) {
261 host->cmd_flags |= SCMD_ACTIVE;
262 writel(TIFM_MMCSD_EOFB
263 | readl(sock->addr
264 + SOCK_MMCSD_INT_ENABLE),
265 sock->addr
266 + SOCK_MMCSD_INT_ENABLE);
290 tifm_sd_exec(host, host->req->stop); 267 tifm_sd_exec(host, host->req->stop);
291 host->state = SCMD; 268 return;
292 } else { 269 } else {
293 host->state = READY; 270 if (!(host->cmd_flags & SCMD_READY)
271 || (host->cmd_flags & CARD_BUSY))
272 return;
273 writel((~TIFM_MMCSD_EOFB)
274 & readl(sock->addr
275 + SOCK_MMCSD_INT_ENABLE),
276 sock->addr
277 + SOCK_MMCSD_INT_ENABLE);
294 } 278 }
295 } else { 279 } else {
296 host->state = FIFO; 280 if (host->cmd_flags & CARD_BUSY)
281 return;
282 writel((~TIFM_MMCSD_EOFB)
283 & readl(sock->addr
284 + SOCK_MMCSD_INT_ENABLE),
285 sock->addr + SOCK_MMCSD_INT_ENABLE);
297 } 286 }
298 goto change_state; 287 } else {
299 }
300 break;
301 case FIFO:
302 if (host->cmd_flags & FIFO_RDY) {
303 host->cmd_flags &= ~FIFO_RDY;
304 if (host->req->stop) { 288 if (host->req->stop) {
305 tifm_sd_exec(host, host->req->stop); 289 if (!(host->cmd_flags & SCMD_ACTIVE)) {
306 host->state = SCMD; 290 host->cmd_flags |= SCMD_ACTIVE;
307 } else { 291 tifm_sd_exec(host, host->req->stop);
308 host->state = READY; 292 return;
293 } else {
294 if (!(host->cmd_flags & SCMD_READY))
295 return;
296 }
309 } 297 }
310 goto change_state;
311 } 298 }
312 break;
313 case READY:
314 tasklet_schedule(&host->finish_tasklet);
315 return;
316 } 299 }
317 300finish_request:
301 tasklet_schedule(&host->finish_tasklet);
318} 302}
319 303
320/* Called from interrupt handler */ 304/* Called from interrupt handler */
@@ -322,21 +306,25 @@ static void tifm_sd_data_event(struct tifm_dev *sock)
322{ 306{
323 struct tifm_sd *host; 307 struct tifm_sd *host;
324 unsigned int fifo_status = 0; 308 unsigned int fifo_status = 0;
309 struct mmc_data *r_data = NULL;
325 310
326 spin_lock(&sock->lock); 311 spin_lock(&sock->lock);
327 host = mmc_priv((struct mmc_host*)tifm_get_drvdata(sock)); 312 host = mmc_priv((struct mmc_host*)tifm_get_drvdata(sock));
328
329 fifo_status = readl(sock->addr + SOCK_DMA_FIFO_STATUS); 313 fifo_status = readl(sock->addr + SOCK_DMA_FIFO_STATUS);
330 writel(fifo_status, sock->addr + SOCK_DMA_FIFO_STATUS); 314 dev_dbg(&sock->dev, "data event: fifo_status %x, flags %x\n",
315 fifo_status, host->cmd_flags);
331 316
332 host->cmd_flags |= fifo_status & FIFO_RDY; 317 if (host->req) {
318 r_data = host->req->cmd->data;
333 319
334 if (host->req) 320 if (r_data && (fifo_status & TIFM_FIFO_READY)) {
335 tifm_sd_process_cmd(sock, host, 0); 321 host->cmd_flags |= FIFO_READY;
322 tifm_sd_check_status(host);
323 }
324 }
336 325
337 dev_dbg(&sock->dev, "fifo_status %x\n", fifo_status); 326 writel(fifo_status, sock->addr + SOCK_DMA_FIFO_STATUS);
338 spin_unlock(&sock->lock); 327 spin_unlock(&sock->lock);
339
340} 328}
341 329
342/* Called from interrupt handler */ 330/* Called from interrupt handler */
@@ -344,60 +332,88 @@ static void tifm_sd_card_event(struct tifm_dev *sock)
344{ 332{
345 struct tifm_sd *host; 333 struct tifm_sd *host;
346 unsigned int host_status = 0; 334 unsigned int host_status = 0;
347 int error_code = 0; 335 int cmd_error = MMC_ERR_NONE;
336 struct mmc_command *cmd = NULL;
337 unsigned long flags;
348 338
349 spin_lock(&sock->lock); 339 spin_lock(&sock->lock);
350 host = mmc_priv((struct mmc_host*)tifm_get_drvdata(sock)); 340 host = mmc_priv((struct mmc_host*)tifm_get_drvdata(sock));
341 host_status = readl(sock->addr + SOCK_MMCSD_STATUS);
342 dev_dbg(&sock->dev, "host event: host_status %x, flags %x\n",
343 host_status, host->cmd_flags);
351 344
352 345 if (host->req) {
353 host_status = readl(sock->addr + SOCK_MMCSD_STATUS); 346 cmd = host->req->cmd;
354 writel(host_status, sock->addr + SOCK_MMCSD_STATUS);
355
356 if (!host->req)
357 goto done;
358 347
359 if (host_status & TIFM_MMCSD_ERRMASK) { 348 if (host_status & TIFM_MMCSD_ERRMASK) {
360 if (host_status & (TIFM_MMCSD_CTO | TIFM_MMCSD_DTO)) 349 writel(host_status & TIFM_MMCSD_ERRMASK,
361 error_code = MMC_ERR_TIMEOUT; 350 sock->addr + SOCK_MMCSD_STATUS);
362 else if (host_status 351 if (host_status & TIFM_MMCSD_CTO)
363 & (TIFM_MMCSD_CCRC | TIFM_MMCSD_DCRC)) 352 cmd_error = MMC_ERR_TIMEOUT;
364 error_code = MMC_ERR_BADCRC; 353 else if (host_status & TIFM_MMCSD_CCRC)
354 cmd_error = MMC_ERR_BADCRC;
355
356 if (cmd->data) {
357 if (host_status & TIFM_MMCSD_DTO)
358 cmd->data->error = MMC_ERR_TIMEOUT;
359 else if (host_status & TIFM_MMCSD_DCRC)
360 cmd->data->error = MMC_ERR_BADCRC;
361 }
365 362
366 writel(TIFM_FIFO_INT_SETALL, 363 writel(TIFM_FIFO_INT_SETALL,
367 sock->addr + SOCK_DMA_FIFO_INT_ENABLE_CLEAR); 364 sock->addr + SOCK_DMA_FIFO_INT_ENABLE_CLEAR);
368 writel(TIFM_DMA_RESET, sock->addr + SOCK_DMA_CONTROL); 365 writel(TIFM_DMA_RESET, sock->addr + SOCK_DMA_CONTROL);
369 366
370 if (host->req->stop) { 367 if (host->req->stop) {
371 if (host->state == SCMD) { 368 if (host->cmd_flags & SCMD_ACTIVE) {
372 host->req->stop->error = error_code; 369 host->req->stop->error = cmd_error;
373 } else if (host->state == BRS 370 host->cmd_flags |= SCMD_READY;
374 || host->state == CARD 371 } else {
375 || host->state == FIFO) { 372 cmd->error = cmd_error;
376 host->req->cmd->error = error_code; 373 host->cmd_flags |= SCMD_ACTIVE;
377 tifm_sd_exec(host, host->req->stop); 374 tifm_sd_exec(host, host->req->stop);
378 host->state = SCMD;
379 goto done; 375 goto done;
380 } else {
381 host->req->cmd->error = error_code;
382 } 376 }
383 } else { 377 } else
384 host->req->cmd->error = error_code; 378 cmd->error = cmd_error;
379 } else {
380 if (host_status & (TIFM_MMCSD_EOC | TIFM_MMCSD_CERR)) {
381 if (!(host->cmd_flags & CMD_READY)) {
382 host->cmd_flags |= CMD_READY;
383 tifm_sd_fetch_resp(cmd, sock);
384 } else if (host->cmd_flags & SCMD_ACTIVE) {
385 host->cmd_flags |= SCMD_READY;
386 tifm_sd_fetch_resp(host->req->stop,
387 sock);
388 }
385 } 389 }
386 host->state = READY; 390 if (host_status & TIFM_MMCSD_BRS)
391 host->cmd_flags |= BRS_READY;
387 } 392 }
388 393
389 if (host_status & TIFM_MMCSD_CB) 394 if (host->no_dma && cmd->data) {
390 host->cmd_flags |= CARD_BUSY; 395 if (host_status & TIFM_MMCSD_AE)
391 if ((host_status & TIFM_MMCSD_EOFB) 396 writel(host_status & TIFM_MMCSD_AE,
392 && (host->cmd_flags & CARD_BUSY)) { 397 sock->addr + SOCK_MMCSD_STATUS);
393 host->written_blocks++; 398
394 host->cmd_flags &= ~CARD_BUSY; 399 if (host_status & (TIFM_MMCSD_AE | TIFM_MMCSD_AF
400 | TIFM_MMCSD_BRS)) {
401 local_irq_save(flags);
402 tifm_sd_transfer_data(sock, host, host_status);
403 local_irq_restore(flags);
404 host_status &= ~TIFM_MMCSD_AE;
405 }
395 } 406 }
396 407
397 if (host->req) 408 if (host_status & TIFM_MMCSD_EOFB)
398 tifm_sd_process_cmd(sock, host, host_status); 409 host->cmd_flags &= ~CARD_BUSY;
410 else if (host_status & TIFM_MMCSD_CB)
411 host->cmd_flags |= CARD_BUSY;
412
413 tifm_sd_check_status(host);
414 }
399done: 415done:
400 dev_dbg(&sock->dev, "host_status %x\n", host_status); 416 writel(host_status, sock->addr + SOCK_MMCSD_STATUS);
401 spin_unlock(&sock->lock); 417 spin_unlock(&sock->lock);
402} 418}
403 419
@@ -522,7 +538,7 @@ static void tifm_sd_request(struct mmc_host *mmc, struct mmc_request *mrq)
522 538
523 host->req = mrq; 539 host->req = mrq;
524 mod_timer(&host->timer, jiffies + host->timeout_jiffies); 540 mod_timer(&host->timer, jiffies + host->timeout_jiffies);
525 host->state = CMD; 541 host->cmd_flags = 0;
526 writel(TIFM_CTRL_LED | readl(sock->addr + SOCK_CONTROL), 542 writel(TIFM_CTRL_LED | readl(sock->addr + SOCK_CONTROL),
527 sock->addr + SOCK_CONTROL); 543 sock->addr + SOCK_CONTROL);
528 tifm_sd_exec(host, mrq->cmd); 544 tifm_sd_exec(host, mrq->cmd);
@@ -553,7 +569,6 @@ static void tifm_sd_end_cmd(unsigned long data)
553 del_timer(&host->timer); 569 del_timer(&host->timer);
554 mrq = host->req; 570 mrq = host->req;
555 host->req = NULL; 571 host->req = NULL;
556 host->state = IDLE;
557 572
558 if (!mrq) { 573 if (!mrq) {
559 printk(KERN_ERR DRIVER_NAME ": no request to complete?\n"); 574 printk(KERN_ERR DRIVER_NAME ": no request to complete?\n");
diff --git a/include/linux/tifm.h b/include/linux/tifm.h
index 82da028d8c07..c8449fcea0c7 100644
--- a/include/linux/tifm.h
+++ b/include/linux/tifm.h
@@ -67,6 +67,7 @@ enum {
67#define TIFM_SOCK_STATE_POWERED 0x00000080 67#define TIFM_SOCK_STATE_POWERED 0x00000080
68 68
69#define TIFM_FIFO_ENABLE 0x00000001 /* Meaning of this constant is unverified */ 69#define TIFM_FIFO_ENABLE 0x00000001 /* Meaning of this constant is unverified */
70#define TIFM_FIFO_READY 0x00000001 /* Meaning of this constant is unverified */
70#define TIFM_FIFO_INT_SETALL 0x0000ffff 71#define TIFM_FIFO_INT_SETALL 0x0000ffff
71#define TIFM_FIFO_INTMASK 0x00000005 /* Meaning of this constant is unverified */ 72#define TIFM_FIFO_INTMASK 0x00000005 /* Meaning of this constant is unverified */
72 73