diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/mmc/tifm_sd.c | 259 |
1 files changed, 137 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 | ||
73 | typedef 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 | |||
83 | enum { | 73 | enum { |
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 | ||
88 | struct tifm_sd { | 83 | struct 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 | ||
237 | static void tifm_sd_process_cmd(struct tifm_dev *sock, struct tifm_sd *host, | 232 | static 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 | ||
242 | change_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 | 300 | finish_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 | } | ||
399 | done: | 415 | done: |
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"); |