diff options
author | James Hogan <james.hogan@imgtec.com> | 2011-06-29 12:28:43 +0400 |
---|---|---|
committer | Chris Ball <cjb@laptop.org> | 2011-07-21 01:21:05 +0400 |
commit | 03e8cb534e7cc3f71a07528a44da7ce68e5b5708 (patch) | |
tree | 989d0a96a03d8838de7617f6eb0062ed2a7065e6 | |
parent | 65d13516b2358c38ac56a5f83e989a6837dcf825 (diff) | |
download | linux-03e8cb534e7cc3f71a07528a44da7ce68e5b5708.tar.xz |
mmc: dw_mmc: fix stop when fallen back to PIO
There are several situations when dw_mci_submit_data_dma() decides to
fall back to PIO mode instead of using DMA, due to a short (to avoid
overhead) or "complex" (e.g. with unaligned buffers) transaction, even
though host->use_dma is set. However dw_mci_stop_dma() decides whether
to stop DMA or set the EVENT_XFER_COMPLETE event based on host->use_dma.
When falling back to PIO mode this results in data timeout errors
getting missed and the driver locking up.
Therefore add host->using_dma to indicate whether the current
transaction is using dma or not, and adjust dw_mci_stop_dma() to use
that instead.
Signed-off-by: James Hogan <james.hogan@imgtec.com>
Acked-by: Will Newton <will.newton@imgtec.com>
Tested-by: Jaehoon Chung <jh80.chung@samsung.com>
Signed-off-by: Chris Ball <cjb@laptop.org>
-rw-r--r-- | drivers/mmc/host/dw_mmc.c | 6 | ||||
-rw-r--r-- | include/linux/mmc/dw_mmc.h | 2 |
2 files changed, 7 insertions, 1 deletions
diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 10b697986283..fcff3c042f69 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -287,7 +287,7 @@ static void send_stop_cmd(struct dw_mci *host, struct mmc_data *data) /* DMA interface functions */ static void dw_mci_stop_dma(struct dw_mci *host) { - if (host->use_dma) { + if (host->using_dma) { host->dma_ops->stop(host); host->dma_ops->cleanup(host); } else { @@ -435,6 +435,8 @@ static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data) unsigned int i, direction, sg_len; u32 temp; + host->using_dma = 0; + /* If we don't have a channel, we can't do DMA */ if (!host->use_dma) return -ENODEV; @@ -454,6 +456,8 @@ static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data) return -EINVAL; } + host->using_dma = 1; + if (data->flags & MMC_DATA_READ) direction = DMA_FROM_DEVICE; else diff --git a/include/linux/mmc/dw_mmc.h b/include/linux/mmc/dw_mmc.h index f3f68ee08a1e..6b46819705d1 100644 --- a/include/linux/mmc/dw_mmc.h +++ b/include/linux/mmc/dw_mmc.h @@ -48,6 +48,7 @@ struct mmc_data; * @data: The data currently being transferred, or NULL if no data * transfer is in progress. * @use_dma: Whether DMA channel is initialized or not. + * @using_dma: Whether DMA is in use for the current transfer. * @sg_dma: Bus address of DMA buffer. * @sg_cpu: Virtual address of DMA buffer. * @dma_ops: Pointer to platform-specific DMA callbacks. @@ -121,6 +122,7 @@ struct dw_mci { /* DMA interface members*/ int use_dma; + int using_dma; dma_addr_t sg_dma; void *sg_cpu; |