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>
This commit is contained in:
@@ -287,7 +287,7 @@ static void send_stop_cmd(struct dw_mci *host, struct mmc_data *data)
|
|||||||
/* DMA interface functions */
|
/* DMA interface functions */
|
||||||
static void dw_mci_stop_dma(struct dw_mci *host)
|
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->stop(host);
|
||||||
host->dma_ops->cleanup(host);
|
host->dma_ops->cleanup(host);
|
||||||
} else {
|
} 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;
|
unsigned int i, direction, sg_len;
|
||||||
u32 temp;
|
u32 temp;
|
||||||
|
|
||||||
|
host->using_dma = 0;
|
||||||
|
|
||||||
/* If we don't have a channel, we can't do DMA */
|
/* If we don't have a channel, we can't do DMA */
|
||||||
if (!host->use_dma)
|
if (!host->use_dma)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
@@ -454,6 +456,8 @@ static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data)
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
host->using_dma = 1;
|
||||||
|
|
||||||
if (data->flags & MMC_DATA_READ)
|
if (data->flags & MMC_DATA_READ)
|
||||||
direction = DMA_FROM_DEVICE;
|
direction = DMA_FROM_DEVICE;
|
||||||
else
|
else
|
||||||
|
@@ -48,6 +48,7 @@ struct mmc_data;
|
|||||||
* @data: The data currently being transferred, or NULL if no data
|
* @data: The data currently being transferred, or NULL if no data
|
||||||
* transfer is in progress.
|
* transfer is in progress.
|
||||||
* @use_dma: Whether DMA channel is initialized or not.
|
* @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_dma: Bus address of DMA buffer.
|
||||||
* @sg_cpu: Virtual address of DMA buffer.
|
* @sg_cpu: Virtual address of DMA buffer.
|
||||||
* @dma_ops: Pointer to platform-specific DMA callbacks.
|
* @dma_ops: Pointer to platform-specific DMA callbacks.
|
||||||
@@ -121,6 +122,7 @@ struct dw_mci {
|
|||||||
|
|
||||||
/* DMA interface members*/
|
/* DMA interface members*/
|
||||||
int use_dma;
|
int use_dma;
|
||||||
|
int using_dma;
|
||||||
|
|
||||||
dma_addr_t sg_dma;
|
dma_addr_t sg_dma;
|
||||||
void *sg_cpu;
|
void *sg_cpu;
|
||||||
|
Reference in New Issue
Block a user