diff options
Diffstat (limited to 'drivers/mmc/host/mmci.c')
| -rw-r--r-- | drivers/mmc/host/mmci.c | 147 | 
1 files changed, 134 insertions, 13 deletions
diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index fe140724a02..fef7140eb1d 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -226,6 +226,9 @@ static void __devinit mmci_dma_setup(struct mmci_host *host)  		return;  	} +	/* initialize pre request cookie */ +	host->next_data.cookie = 1; +  	/* Try to acquire a generic DMA engine slave channel */  	dma_cap_zero(mask);  	dma_cap_set(DMA_SLAVE, mask); @@ -335,7 +338,8 @@ static void mmci_dma_unmap(struct mmci_host *host, struct mmc_data *data)  		dir = DMA_FROM_DEVICE;  	} -	dma_unmap_sg(chan->device->dev, data->sg, data->sg_len, dir); +	if (!data->host_cookie) +		dma_unmap_sg(chan->device->dev, data->sg, data->sg_len, dir);  	/*  	 * Use of DMA with scatter-gather is impossible. @@ -353,7 +357,8 @@ static void mmci_dma_data_error(struct mmci_host *host)  	dmaengine_terminate_all(host->dma_current);  } -static int mmci_dma_start_data(struct mmci_host *host, unsigned int datactrl) +static int mmci_dma_prep_data(struct mmci_host *host, struct mmc_data *data, +			      struct mmci_host_next *next)  {  	struct variant_data *variant = host->variant;  	struct dma_slave_config conf = { @@ -364,13 +369,20 @@ static int mmci_dma_start_data(struct mmci_host *host, unsigned int datactrl)  		.src_maxburst = variant->fifohalfsize >> 2, /* # of words */  		.dst_maxburst = variant->fifohalfsize >> 2, /* # of words */  	}; -	struct mmc_data *data = host->data;  	struct dma_chan *chan;  	struct dma_device *device;  	struct dma_async_tx_descriptor *desc;  	int nr_sg; -	host->dma_current = NULL; +	/* Check if next job is already prepared */ +	if (data->host_cookie && !next && +	    host->dma_current && host->dma_desc_current) +		return 0; + +	if (!next) { +		host->dma_current = NULL; +		host->dma_desc_current = NULL; +	}  	if (data->flags & MMC_DATA_READ) {  		conf.direction = DMA_FROM_DEVICE; @@ -385,7 +397,7 @@ static int mmci_dma_start_data(struct mmci_host *host, unsigned int datactrl)  		return -EINVAL;  	/* If less than or equal to the fifo size, don't bother with DMA */ -	if (host->size <= variant->fifosize) +	if (data->blksz * data->blocks <= variant->fifosize)  		return -EINVAL;  	device = chan->device; @@ -399,14 +411,38 @@ static int mmci_dma_start_data(struct mmci_host *host, unsigned int datactrl)  	if (!desc)  		goto unmap_exit; -	/* Okay, go for it. */ -	host->dma_current = chan; +	if (next) { +		next->dma_chan = chan; +		next->dma_desc = desc; +	} else { +		host->dma_current = chan; +		host->dma_desc_current = desc; +	} + +	return 0; + unmap_exit: +	if (!next) +		dmaengine_terminate_all(chan); +	dma_unmap_sg(device->dev, data->sg, data->sg_len, conf.direction); +	return -ENOMEM; +} + +static int mmci_dma_start_data(struct mmci_host *host, unsigned int datactrl) +{ +	int ret; +	struct mmc_data *data = host->data; + +	ret = mmci_dma_prep_data(host, host->data, NULL); +	if (ret) +		return ret; + +	/* Okay, go for it. */  	dev_vdbg(mmc_dev(host->mmc),  		 "Submit MMCI DMA job, sglen %d blksz %04x blks %04x flags %08x\n",  		 data->sg_len, data->blksz, data->blocks, data->flags); -	dmaengine_submit(desc); -	dma_async_issue_pending(chan); +	dmaengine_submit(host->dma_desc_current); +	dma_async_issue_pending(host->dma_current);  	datactrl |= MCI_DPSM_DMAENABLE; @@ -421,14 +457,90 @@ static int mmci_dma_start_data(struct mmci_host *host, unsigned int datactrl)  	writel(readl(host->base + MMCIMASK0) | MCI_DATAENDMASK,  	       host->base + MMCIMASK0);  	return 0; +} -unmap_exit: -	dmaengine_terminate_all(chan); -	dma_unmap_sg(device->dev, data->sg, data->sg_len, conf.direction); -	return -ENOMEM; +static void mmci_get_next_data(struct mmci_host *host, struct mmc_data *data) +{ +	struct mmci_host_next *next = &host->next_data; + +	if (data->host_cookie && data->host_cookie != next->cookie) { +		printk(KERN_WARNING "[%s] invalid cookie: data->host_cookie %d" +		       " host->next_data.cookie %d\n", +		       __func__, data->host_cookie, host->next_data.cookie); +		data->host_cookie = 0; +	} + +	if (!data->host_cookie) +		return; + +	host->dma_desc_current = next->dma_desc; +	host->dma_current = next->dma_chan; + +	next->dma_desc = NULL; +	next->dma_chan = NULL;  } + +static void mmci_pre_request(struct mmc_host *mmc, struct mmc_request *mrq, +			     bool is_first_req) +{ +	struct mmci_host *host = mmc_priv(mmc); +	struct mmc_data *data = mrq->data; +	struct mmci_host_next *nd = &host->next_data; + +	if (!data) +		return; + +	if (data->host_cookie) { +		data->host_cookie = 0; +		return; +	} + +	/* if config for dma */ +	if (((data->flags & MMC_DATA_WRITE) && host->dma_tx_channel) || +	    ((data->flags & MMC_DATA_READ) && host->dma_rx_channel)) { +		if (mmci_dma_prep_data(host, data, nd)) +			data->host_cookie = 0; +		else +			data->host_cookie = ++nd->cookie < 0 ? 1 : nd->cookie; +	} +} + +static void mmci_post_request(struct mmc_host *mmc, struct mmc_request *mrq, +			      int err) +{ +	struct mmci_host *host = mmc_priv(mmc); +	struct mmc_data *data = mrq->data; +	struct dma_chan *chan; +	enum dma_data_direction dir; + +	if (!data) +		return; + +	if (data->flags & MMC_DATA_READ) { +		dir = DMA_FROM_DEVICE; +		chan = host->dma_rx_channel; +	} else { +		dir = DMA_TO_DEVICE; +		chan = host->dma_tx_channel; +	} + + +	/* if config for dma */ +	if (chan) { +		if (err) +			dmaengine_terminate_all(chan); +		if (err || data->host_cookie) +			dma_unmap_sg(mmc_dev(host->mmc), data->sg, +				     data->sg_len, dir); +		mrq->data->host_cookie = 0; +	} +} +  #else  /* Blank functions if the DMA engine is not available */ +static void mmci_get_next_data(struct mmci_host *host, struct mmc_data *data) +{ +}  static inline void mmci_dma_setup(struct mmci_host *host)  {  } @@ -449,6 +561,10 @@ static inline int mmci_dma_start_data(struct mmci_host *host, unsigned int datac  {  	return -ENOSYS;  } + +#define mmci_pre_request NULL +#define mmci_post_request NULL +  #endif  static void mmci_start_data(struct mmci_host *host, struct mmc_data *data) @@ -872,6 +988,9 @@ static void mmci_request(struct mmc_host *mmc, struct mmc_request *mrq)  	host->mrq = mrq; +	if (mrq->data) +		mmci_get_next_data(host, mrq->data); +  	if (mrq->data && mrq->data->flags & MMC_DATA_READ)  		mmci_start_data(host, mrq->data); @@ -986,6 +1105,8 @@ static irqreturn_t mmci_cd_irq(int irq, void *dev_id)  static const struct mmc_host_ops mmci_ops = {  	.request	= mmci_request, +	.pre_req	= mmci_pre_request, +	.post_req	= mmci_post_request,  	.set_ios	= mmci_set_ios,  	.get_ro		= mmci_get_ro,  	.get_cd		= mmci_get_cd,  |