diff options
author | Pierre Ossman <drzeus@drzeus.cx> | 2008-06-30 15:15:49 -0400 |
---|---|---|
committer | Pierre Ossman <drzeus@drzeus.cx> | 2008-07-15 08:14:45 -0400 |
commit | 8f1934ce784bd8f2eaf06f190526500f7f3f9c74 (patch) | |
tree | b65e80bdfb3b8212e43f87947d2a7c4ef4b7d7ee /drivers/mmc | |
parent | 6b174931a73177c6519f87e6a8d5ae6ba269cdb5 (diff) |
sdhci: graceful handling of bad addresses
Be a bit more robust and fall back to PIO if someone is feeding us
bogus addresses.
Signed-off-by: Pierre Ossman <drzeus@drzeus.cx>
Diffstat (limited to 'drivers/mmc')
-rw-r--r-- | drivers/mmc/host/sdhci.c | 77 |
1 files changed, 57 insertions, 20 deletions
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index b802044ea940..d3e4a391e351 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c | |||
@@ -327,7 +327,7 @@ static void sdhci_kunmap_atomic(void *buffer, unsigned long *flags) | |||
327 | local_irq_restore(*flags); | 327 | local_irq_restore(*flags); |
328 | } | 328 | } |
329 | 329 | ||
330 | static void sdhci_adma_table_pre(struct sdhci_host *host, | 330 | static int sdhci_adma_table_pre(struct sdhci_host *host, |
331 | struct mmc_data *data) | 331 | struct mmc_data *data) |
332 | { | 332 | { |
333 | int direction; | 333 | int direction; |
@@ -360,10 +360,14 @@ static void sdhci_adma_table_pre(struct sdhci_host *host, | |||
360 | 360 | ||
361 | host->align_addr = dma_map_single(mmc_dev(host->mmc), | 361 | host->align_addr = dma_map_single(mmc_dev(host->mmc), |
362 | host->align_buffer, 128 * 4, direction); | 362 | host->align_buffer, 128 * 4, direction); |
363 | if (dma_mapping_error(host->align_addr)) | ||
364 | goto fail; | ||
363 | BUG_ON(host->align_addr & 0x3); | 365 | BUG_ON(host->align_addr & 0x3); |
364 | 366 | ||
365 | host->sg_count = dma_map_sg(mmc_dev(host->mmc), | 367 | host->sg_count = dma_map_sg(mmc_dev(host->mmc), |
366 | data->sg, data->sg_len, direction); | 368 | data->sg, data->sg_len, direction); |
369 | if (host->sg_count == 0) | ||
370 | goto unmap_align; | ||
367 | 371 | ||
368 | desc = host->adma_desc; | 372 | desc = host->adma_desc; |
369 | align = host->align_buffer; | 373 | align = host->align_buffer; |
@@ -457,7 +461,20 @@ static void sdhci_adma_table_pre(struct sdhci_host *host, | |||
457 | 461 | ||
458 | host->adma_addr = dma_map_single(mmc_dev(host->mmc), | 462 | host->adma_addr = dma_map_single(mmc_dev(host->mmc), |
459 | host->adma_desc, (128 * 2 + 1) * 4, DMA_TO_DEVICE); | 463 | host->adma_desc, (128 * 2 + 1) * 4, DMA_TO_DEVICE); |
464 | if (dma_mapping_error(host->align_addr)) | ||
465 | goto unmap_entries; | ||
460 | BUG_ON(host->adma_addr & 0x3); | 466 | BUG_ON(host->adma_addr & 0x3); |
467 | |||
468 | return 0; | ||
469 | |||
470 | unmap_entries: | ||
471 | dma_unmap_sg(mmc_dev(host->mmc), data->sg, | ||
472 | data->sg_len, direction); | ||
473 | unmap_align: | ||
474 | dma_unmap_single(mmc_dev(host->mmc), host->align_addr, | ||
475 | 128 * 4, direction); | ||
476 | fail: | ||
477 | return -EINVAL; | ||
461 | } | 478 | } |
462 | 479 | ||
463 | static void sdhci_adma_table_post(struct sdhci_host *host, | 480 | static void sdhci_adma_table_post(struct sdhci_host *host, |
@@ -555,6 +572,7 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data) | |||
555 | { | 572 | { |
556 | u8 count; | 573 | u8 count; |
557 | u8 ctrl; | 574 | u8 ctrl; |
575 | int ret; | ||
558 | 576 | ||
559 | WARN_ON(host->data); | 577 | WARN_ON(host->data); |
560 | 578 | ||
@@ -639,6 +657,43 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data) | |||
639 | } | 657 | } |
640 | } | 658 | } |
641 | 659 | ||
660 | if (host->flags & SDHCI_REQ_USE_DMA) { | ||
661 | if (host->flags & SDHCI_USE_ADMA) { | ||
662 | ret = sdhci_adma_table_pre(host, data); | ||
663 | if (ret) { | ||
664 | /* | ||
665 | * This only happens when someone fed | ||
666 | * us an invalid request. | ||
667 | */ | ||
668 | WARN_ON(1); | ||
669 | host->flags &= ~SDHCI_USE_DMA; | ||
670 | } else { | ||
671 | writel(host->adma_addr, | ||
672 | host->ioaddr + SDHCI_ADMA_ADDRESS); | ||
673 | } | ||
674 | } else { | ||
675 | int count; | ||
676 | |||
677 | count = dma_map_sg(mmc_dev(host->mmc), | ||
678 | data->sg, data->sg_len, | ||
679 | (data->flags & MMC_DATA_READ) ? | ||
680 | DMA_FROM_DEVICE : | ||
681 | DMA_TO_DEVICE); | ||
682 | if (count == 0) { | ||
683 | /* | ||
684 | * This only happens when someone fed | ||
685 | * us an invalid request. | ||
686 | */ | ||
687 | WARN_ON(1); | ||
688 | host->flags &= ~SDHCI_USE_DMA; | ||
689 | } else { | ||
690 | WARN_ON(count != 1); | ||
691 | writel(sg_dma_address(data->sg), | ||
692 | host->ioaddr + SDHCI_DMA_ADDRESS); | ||
693 | } | ||
694 | } | ||
695 | } | ||
696 | |||
642 | /* | 697 | /* |
643 | * Always adjust the DMA selection as some controllers | 698 | * Always adjust the DMA selection as some controllers |
644 | * (e.g. JMicron) can't do PIO properly when the selection | 699 | * (e.g. JMicron) can't do PIO properly when the selection |
@@ -655,25 +710,7 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data) | |||
655 | writeb(ctrl, host->ioaddr + SDHCI_HOST_CONTROL); | 710 | writeb(ctrl, host->ioaddr + SDHCI_HOST_CONTROL); |
656 | } | 711 | } |
657 | 712 | ||
658 | if (host->flags & SDHCI_REQ_USE_DMA) { | 713 | if (!(host->flags & SDHCI_REQ_USE_DMA)) { |
659 | if (host->flags & SDHCI_USE_ADMA) { | ||
660 | sdhci_adma_table_pre(host, data); | ||
661 | writel(host->adma_addr, | ||
662 | host->ioaddr + SDHCI_ADMA_ADDRESS); | ||
663 | } else { | ||
664 | int count; | ||
665 | |||
666 | count = dma_map_sg(mmc_dev(host->mmc), | ||
667 | data->sg, data->sg_len, | ||
668 | (data->flags & MMC_DATA_READ) ? | ||
669 | DMA_FROM_DEVICE : | ||
670 | DMA_TO_DEVICE); | ||
671 | WARN_ON(count != 1); | ||
672 | |||
673 | writel(sg_dma_address(data->sg), | ||
674 | host->ioaddr + SDHCI_DMA_ADDRESS); | ||
675 | } | ||
676 | } else { | ||
677 | host->cur_sg = data->sg; | 714 | host->cur_sg = data->sg; |
678 | host->num_sg = data->sg_len; | 715 | host->num_sg = data->sg_len; |
679 | 716 | ||