diff options
author | Seungwon Jeon <tgih.jun@samsung.com> | 2012-02-06 02:55:07 -0500 |
---|---|---|
committer | Chris Ball <cjb@laptop.org> | 2012-03-25 19:33:45 -0400 |
commit | 9aa514089ed4f3a943ab9445e23e03a7be3044e6 (patch) | |
tree | 4d2d643f683cc430d8a47a8769b888118609be70 /drivers/mmc/host/dw_mmc.c | |
parent | 3e44a1a7d22cdc44c98569fedbd993985bfd64d3 (diff) |
mmc: dw_mmc: Add support for pre_req and post_req
This patch implements pre_req and post_req in dw_mmc to support
asynchronous mmc request.
Signed-off-by: Seungwon Jeon <tgih.jun@samsung.com>
Acked-by: Jaehoon Chung <jh80.chung@samsung.com>
Acked-by: Will Newton <will.newton@imgtec.com>
Signed-off-by: Chris Ball <cjb@laptop.org>
Diffstat (limited to 'drivers/mmc/host/dw_mmc.c')
-rw-r--r-- | drivers/mmc/host/dw_mmc.c | 129 |
1 files changed, 102 insertions, 27 deletions
diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index d1edf15b29fc..7d6ad6cd4a50 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c | |||
@@ -296,14 +296,24 @@ static void dw_mci_stop_dma(struct dw_mci *host) | |||
296 | } | 296 | } |
297 | 297 | ||
298 | #ifdef CONFIG_MMC_DW_IDMAC | 298 | #ifdef CONFIG_MMC_DW_IDMAC |
299 | static int dw_mci_get_dma_dir(struct mmc_data *data) | ||
300 | { | ||
301 | if (data->flags & MMC_DATA_WRITE) | ||
302 | return DMA_TO_DEVICE; | ||
303 | else | ||
304 | return DMA_FROM_DEVICE; | ||
305 | } | ||
306 | |||
299 | static void dw_mci_dma_cleanup(struct dw_mci *host) | 307 | static void dw_mci_dma_cleanup(struct dw_mci *host) |
300 | { | 308 | { |
301 | struct mmc_data *data = host->data; | 309 | struct mmc_data *data = host->data; |
302 | 310 | ||
303 | if (data) | 311 | if (data) |
304 | dma_unmap_sg(&host->dev, data->sg, data->sg_len, | 312 | if (!data->host_cookie) |
305 | ((data->flags & MMC_DATA_WRITE) | 313 | dma_unmap_sg(&host->dev, |
306 | ? DMA_TO_DEVICE : DMA_FROM_DEVICE)); | 314 | data->sg, |
315 | data->sg_len, | ||
316 | dw_mci_get_dma_dir(data)); | ||
307 | } | 317 | } |
308 | 318 | ||
309 | static void dw_mci_idmac_stop_dma(struct dw_mci *host) | 319 | static void dw_mci_idmac_stop_dma(struct dw_mci *host) |
@@ -419,26 +429,15 @@ static int dw_mci_idmac_init(struct dw_mci *host) | |||
419 | return 0; | 429 | return 0; |
420 | } | 430 | } |
421 | 431 | ||
422 | static struct dw_mci_dma_ops dw_mci_idmac_ops = { | 432 | static int dw_mci_pre_dma_transfer(struct dw_mci *host, |
423 | .init = dw_mci_idmac_init, | 433 | struct mmc_data *data, |
424 | .start = dw_mci_idmac_start_dma, | 434 | bool next) |
425 | .stop = dw_mci_idmac_stop_dma, | ||
426 | .complete = dw_mci_idmac_complete_dma, | ||
427 | .cleanup = dw_mci_dma_cleanup, | ||
428 | }; | ||
429 | #endif /* CONFIG_MMC_DW_IDMAC */ | ||
430 | |||
431 | static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data) | ||
432 | { | 435 | { |
433 | struct scatterlist *sg; | 436 | struct scatterlist *sg; |
434 | unsigned int i, direction, sg_len; | 437 | unsigned int i, sg_len; |
435 | u32 temp; | ||
436 | |||
437 | host->using_dma = 0; | ||
438 | 438 | ||
439 | /* If we don't have a channel, we can't do DMA */ | 439 | if (!next && data->host_cookie) |
440 | if (!host->use_dma) | 440 | return data->host_cookie; |
441 | return -ENODEV; | ||
442 | 441 | ||
443 | /* | 442 | /* |
444 | * We don't do DMA on "complex" transfers, i.e. with | 443 | * We don't do DMA on "complex" transfers, i.e. with |
@@ -447,6 +446,7 @@ static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data) | |||
447 | */ | 446 | */ |
448 | if (data->blocks * data->blksz < DW_MCI_DMA_THRESHOLD) | 447 | if (data->blocks * data->blksz < DW_MCI_DMA_THRESHOLD) |
449 | return -EINVAL; | 448 | return -EINVAL; |
449 | |||
450 | if (data->blksz & 3) | 450 | if (data->blksz & 3) |
451 | return -EINVAL; | 451 | return -EINVAL; |
452 | 452 | ||
@@ -455,15 +455,88 @@ static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data) | |||
455 | return -EINVAL; | 455 | return -EINVAL; |
456 | } | 456 | } |
457 | 457 | ||
458 | host->using_dma = 1; | 458 | sg_len = dma_map_sg(&host->dev, |
459 | data->sg, | ||
460 | data->sg_len, | ||
461 | dw_mci_get_dma_dir(data)); | ||
462 | if (sg_len == 0) | ||
463 | return -EINVAL; | ||
459 | 464 | ||
460 | if (data->flags & MMC_DATA_READ) | 465 | if (next) |
461 | direction = DMA_FROM_DEVICE; | 466 | data->host_cookie = sg_len; |
462 | else | 467 | |
463 | direction = DMA_TO_DEVICE; | 468 | return sg_len; |
469 | } | ||
470 | |||
471 | static struct dw_mci_dma_ops dw_mci_idmac_ops = { | ||
472 | .init = dw_mci_idmac_init, | ||
473 | .start = dw_mci_idmac_start_dma, | ||
474 | .stop = dw_mci_idmac_stop_dma, | ||
475 | .complete = dw_mci_idmac_complete_dma, | ||
476 | .cleanup = dw_mci_dma_cleanup, | ||
477 | }; | ||
478 | #else | ||
479 | static int dw_mci_pre_dma_transfer(struct dw_mci *host, | ||
480 | struct mmc_data *data, | ||
481 | bool next) | ||
482 | { | ||
483 | return -ENOSYS; | ||
484 | } | ||
485 | #endif /* CONFIG_MMC_DW_IDMAC */ | ||
486 | |||
487 | static void dw_mci_pre_req(struct mmc_host *mmc, | ||
488 | struct mmc_request *mrq, | ||
489 | bool is_first_req) | ||
490 | { | ||
491 | struct dw_mci_slot *slot = mmc_priv(mmc); | ||
492 | struct mmc_data *data = mrq->data; | ||
493 | |||
494 | if (!slot->host->use_dma || !data) | ||
495 | return; | ||
496 | |||
497 | if (data->host_cookie) { | ||
498 | data->host_cookie = 0; | ||
499 | return; | ||
500 | } | ||
501 | |||
502 | if (dw_mci_pre_dma_transfer(slot->host, mrq->data, 1) < 0) | ||
503 | data->host_cookie = 0; | ||
504 | } | ||
505 | |||
506 | static void dw_mci_post_req(struct mmc_host *mmc, | ||
507 | struct mmc_request *mrq, | ||
508 | int err) | ||
509 | { | ||
510 | struct dw_mci_slot *slot = mmc_priv(mmc); | ||
511 | struct mmc_data *data = mrq->data; | ||
512 | |||
513 | if (!slot->host->use_dma || !data) | ||
514 | return; | ||
515 | |||
516 | if (data->host_cookie) | ||
517 | dma_unmap_sg(&slot->host->dev, | ||
518 | data->sg, | ||
519 | data->sg_len, | ||
520 | dw_mci_get_dma_dir(data)); | ||
521 | data->host_cookie = 0; | ||
522 | } | ||
523 | |||
524 | static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data) | ||
525 | { | ||
526 | int sg_len; | ||
527 | u32 temp; | ||
464 | 528 | ||
465 | sg_len = dma_map_sg(&host->dev, data->sg, data->sg_len, | 529 | host->using_dma = 0; |
466 | direction); | 530 | |
531 | /* If we don't have a channel, we can't do DMA */ | ||
532 | if (!host->use_dma) | ||
533 | return -ENODEV; | ||
534 | |||
535 | sg_len = dw_mci_pre_dma_transfer(host, data, 0); | ||
536 | if (sg_len < 0) | ||
537 | return sg_len; | ||
538 | |||
539 | host->using_dma = 1; | ||
467 | 540 | ||
468 | dev_vdbg(&host->dev, | 541 | dev_vdbg(&host->dev, |
469 | "sd sg_cpu: %#lx sg_dma: %#lx sg_len: %d\n", | 542 | "sd sg_cpu: %#lx sg_dma: %#lx sg_len: %d\n", |
@@ -800,6 +873,8 @@ static void dw_mci_enable_sdio_irq(struct mmc_host *mmc, int enb) | |||
800 | 873 | ||
801 | static const struct mmc_host_ops dw_mci_ops = { | 874 | static const struct mmc_host_ops dw_mci_ops = { |
802 | .request = dw_mci_request, | 875 | .request = dw_mci_request, |
876 | .pre_req = dw_mci_pre_req, | ||
877 | .post_req = dw_mci_post_req, | ||
803 | .set_ios = dw_mci_set_ios, | 878 | .set_ios = dw_mci_set_ios, |
804 | .get_ro = dw_mci_get_ro, | 879 | .get_ro = dw_mci_get_ro, |
805 | .get_cd = dw_mci_get_cd, | 880 | .get_cd = dw_mci_get_cd, |