aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPierre Ossman <drzeus@drzeus.cx>2008-06-30 15:15:49 -0400
committerPierre Ossman <drzeus@drzeus.cx>2008-07-15 08:14:45 -0400
commit8f1934ce784bd8f2eaf06f190526500f7f3f9c74 (patch)
treeb65e80bdfb3b8212e43f87947d2a7c4ef4b7d7ee
parent6b174931a73177c6519f87e6a8d5ae6ba269cdb5 (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>
-rw-r--r--drivers/mmc/host/sdhci.c77
1 files changed, 57 insertions, 20 deletions
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index b802044ea94..d3e4a391e35 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
330static void sdhci_adma_table_pre(struct sdhci_host *host, 330static 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
470unmap_entries:
471 dma_unmap_sg(mmc_dev(host->mmc), data->sg,
472 data->sg_len, direction);
473unmap_align:
474 dma_unmap_single(mmc_dev(host->mmc), host->align_addr,
475 128 * 4, direction);
476fail:
477 return -EINVAL;
461} 478}
462 479
463static void sdhci_adma_table_post(struct sdhci_host *host, 480static 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