diff options
Diffstat (limited to 'drivers/mmc/host/omap_hsmmc.c')
| -rw-r--r-- | drivers/mmc/host/omap_hsmmc.c | 235 | 
1 files changed, 115 insertions, 120 deletions
diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index 9a7a60aeb19e..3a09f93cc3b6 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -19,6 +19,7 @@  #include <linux/init.h>  #include <linux/kernel.h>  #include <linux/debugfs.h> +#include <linux/dmaengine.h>  #include <linux/seq_file.h>  #include <linux/interrupt.h>  #include <linux/delay.h> @@ -29,6 +30,7 @@  #include <linux/of.h>  #include <linux/of_gpio.h>  #include <linux/of_device.h> +#include <linux/omap-dma.h>  #include <linux/mmc/host.h>  #include <linux/mmc/core.h>  #include <linux/mmc/mmc.h> @@ -37,7 +39,6 @@  #include <linux/gpio.h>  #include <linux/regulator/consumer.h>  #include <linux/pm_runtime.h> -#include <plat/dma.h>  #include <mach/hardware.h>  #include <plat/board.h>  #include <plat/mmc.h> @@ -85,7 +86,6 @@  #define BRR_ENABLE		(1 << 5)  #define DTO_ENABLE		(1 << 20)  #define INIT_STREAM		(1 << 1) -#define ACEN_ACMD12		(1 << 2)  #define DP_SELECT		(1 << 21)  #define DDIR			(1 << 4)  #define DMA_EN			0x1 @@ -117,7 +117,6 @@  #define OMAP_MMC_MAX_CLOCK	52000000  #define DRIVER_NAME		"omap_hsmmc" -#define AUTO_CMD12		(1 << 0)	/* Auto CMD12 support */  /*   * One controller can have multiple slots, like on some omap boards using   * omap.c controller driver. Luckily this is not currently done on any known @@ -168,7 +167,8 @@ struct omap_hsmmc_host {  	int			suspended;  	int			irq;  	int			use_dma, dma_ch; -	int			dma_line_tx, dma_line_rx; +	struct dma_chan		*tx_chan; +	struct dma_chan		*rx_chan;  	int			slot_id;  	int			response_busy;  	int			context_loss; @@ -177,7 +177,6 @@ struct omap_hsmmc_host {  	int			reqs_blocked;  	int			use_reg;  	int			req_in_progress; -	unsigned int		flags;  	struct omap_hsmmc_next	next_data;  	struct	omap_mmc_platform_data	*pdata; @@ -773,8 +772,6 @@ omap_hsmmc_start_command(struct omap_hsmmc_host *host, struct mmc_command *cmd,  		cmdtype = 0x3;  	cmdreg = (cmd->opcode << 24) | (resptype << 16) | (cmdtype << 22); -	if ((host->flags & AUTO_CMD12) && mmc_op_multi(cmd->opcode)) -		cmdreg |= ACEN_ACMD12;  	if (data) {  		cmdreg |= DP_SELECT | MSBS | BCE; @@ -802,6 +799,12 @@ omap_hsmmc_get_dma_dir(struct omap_hsmmc_host *host, struct mmc_data *data)  		return DMA_FROM_DEVICE;  } +static struct dma_chan *omap_hsmmc_get_dma_chan(struct omap_hsmmc_host *host, +	struct mmc_data *data) +{ +	return data->flags & MMC_DATA_WRITE ? host->tx_chan : host->rx_chan; +} +  static void omap_hsmmc_request_done(struct omap_hsmmc_host *host, struct mmc_request *mrq)  {  	int dma_ch; @@ -847,14 +850,11 @@ omap_hsmmc_xfer_done(struct omap_hsmmc_host *host, struct mmc_data *data)  	else  		data->bytes_xfered = 0; -	if (data->stop && ((!(host->flags & AUTO_CMD12)) || data->error)) { -		omap_hsmmc_start_command(host, data->stop, NULL); -	} else { -		if (data->stop) -			data->stop->resp[0] = OMAP_HSMMC_READ(host->base, -							RSP76); +	if (!data->stop) {  		omap_hsmmc_request_done(host, data->mrq); +		return;  	} +	omap_hsmmc_start_command(host, data->stop, NULL);  }  /* @@ -897,10 +897,13 @@ static void omap_hsmmc_dma_cleanup(struct omap_hsmmc_host *host, int errno)  	spin_unlock_irqrestore(&host->irq_lock, flags);  	if (host->use_dma && dma_ch != -1) { -		dma_unmap_sg(mmc_dev(host->mmc), host->data->sg, -			host->data->sg_len, +		struct dma_chan *chan = omap_hsmmc_get_dma_chan(host, host->data); + +		dmaengine_terminate_all(chan); +		dma_unmap_sg(chan->device->dev, +			host->data->sg, host->data->sg_len,  			omap_hsmmc_get_dma_dir(host, host->data)); -		omap_free_dma(dma_ch); +  		host->data->host_cookie = 0;  	}  	host->data = NULL; @@ -1097,7 +1100,7 @@ static int omap_hsmmc_switch_opcond(struct omap_hsmmc_host *host, int vdd)  	/* Disable the clocks */  	pm_runtime_put_sync(host->dev);  	if (host->dbclk) -		clk_disable(host->dbclk); +		clk_disable_unprepare(host->dbclk);  	/* Turn the power off */  	ret = mmc_slot(host).set_power(host->dev, host->slot_id, 0, 0); @@ -1108,7 +1111,7 @@ static int omap_hsmmc_switch_opcond(struct omap_hsmmc_host *host, int vdd)  					       vdd);  	pm_runtime_get_sync(host->dev);  	if (host->dbclk) -		clk_enable(host->dbclk); +		clk_prepare_enable(host->dbclk);  	if (ret != 0)  		goto err; @@ -1198,90 +1201,29 @@ static irqreturn_t omap_hsmmc_detect(int irq, void *dev_id)  	return IRQ_HANDLED;  } -static int omap_hsmmc_get_dma_sync_dev(struct omap_hsmmc_host *host, -				     struct mmc_data *data) -{ -	int sync_dev; - -	if (data->flags & MMC_DATA_WRITE) -		sync_dev = host->dma_line_tx; -	else -		sync_dev = host->dma_line_rx; -	return sync_dev; -} - -static void omap_hsmmc_config_dma_params(struct omap_hsmmc_host *host, -				       struct mmc_data *data, -				       struct scatterlist *sgl) -{ -	int blksz, nblk, dma_ch; - -	dma_ch = host->dma_ch; -	if (data->flags & MMC_DATA_WRITE) { -		omap_set_dma_dest_params(dma_ch, 0, OMAP_DMA_AMODE_CONSTANT, -			(host->mapbase + OMAP_HSMMC_DATA), 0, 0); -		omap_set_dma_src_params(dma_ch, 0, OMAP_DMA_AMODE_POST_INC, -			sg_dma_address(sgl), 0, 0); -	} else { -		omap_set_dma_src_params(dma_ch, 0, OMAP_DMA_AMODE_CONSTANT, -			(host->mapbase + OMAP_HSMMC_DATA), 0, 0); -		omap_set_dma_dest_params(dma_ch, 0, OMAP_DMA_AMODE_POST_INC, -			sg_dma_address(sgl), 0, 0); -	} - -	blksz = host->data->blksz; -	nblk = sg_dma_len(sgl) / blksz; - -	omap_set_dma_transfer_params(dma_ch, OMAP_DMA_DATA_TYPE_S32, -			blksz / 4, nblk, OMAP_DMA_SYNC_FRAME, -			omap_hsmmc_get_dma_sync_dev(host, data), -			!(data->flags & MMC_DATA_WRITE)); - -	omap_start_dma(dma_ch); -} - -/* - * DMA call back function - */ -static void omap_hsmmc_dma_cb(int lch, u16 ch_status, void *cb_data) +static void omap_hsmmc_dma_callback(void *param)  { -	struct omap_hsmmc_host *host = cb_data; +	struct omap_hsmmc_host *host = param; +	struct dma_chan *chan;  	struct mmc_data *data; -	int dma_ch, req_in_progress; -	unsigned long flags; +	int req_in_progress; -	if (!(ch_status & OMAP_DMA_BLOCK_IRQ)) { -		dev_warn(mmc_dev(host->mmc), "unexpected dma status %x\n", -			ch_status); -		return; -	} - -	spin_lock_irqsave(&host->irq_lock, flags); +	spin_lock_irq(&host->irq_lock);  	if (host->dma_ch < 0) { -		spin_unlock_irqrestore(&host->irq_lock, flags); +		spin_unlock_irq(&host->irq_lock);  		return;  	}  	data = host->mrq->data; -	host->dma_sg_idx++; -	if (host->dma_sg_idx < host->dma_len) { -		/* Fire up the next transfer. */ -		omap_hsmmc_config_dma_params(host, data, -					   data->sg + host->dma_sg_idx); -		spin_unlock_irqrestore(&host->irq_lock, flags); -		return; -	} - +	chan = omap_hsmmc_get_dma_chan(host, data);  	if (!data->host_cookie) -		dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len, +		dma_unmap_sg(chan->device->dev, +			     data->sg, data->sg_len,  			     omap_hsmmc_get_dma_dir(host, data));  	req_in_progress = host->req_in_progress; -	dma_ch = host->dma_ch;  	host->dma_ch = -1; -	spin_unlock_irqrestore(&host->irq_lock, flags); - -	omap_free_dma(dma_ch); +	spin_unlock_irq(&host->irq_lock);  	/* If DMA has finished after TC, complete the request */  	if (!req_in_progress) { @@ -1294,7 +1236,8 @@ static void omap_hsmmc_dma_cb(int lch, u16 ch_status, void *cb_data)  static int omap_hsmmc_pre_dma_transfer(struct omap_hsmmc_host *host,  				       struct mmc_data *data, -				       struct omap_hsmmc_next *next) +				       struct omap_hsmmc_next *next, +				       struct dma_chan *chan)  {  	int dma_len; @@ -1309,8 +1252,7 @@ static int omap_hsmmc_pre_dma_transfer(struct omap_hsmmc_host *host,  	/* Check if next job is already prepared */  	if (next ||  	    (!next && data->host_cookie != host->next_data.cookie)) { -		dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg, -				     data->sg_len, +		dma_len = dma_map_sg(chan->device->dev, data->sg, data->sg_len,  				     omap_hsmmc_get_dma_dir(host, data));  	} else { @@ -1337,8 +1279,11 @@ static int omap_hsmmc_pre_dma_transfer(struct omap_hsmmc_host *host,  static int omap_hsmmc_start_dma_transfer(struct omap_hsmmc_host *host,  					struct mmc_request *req)  { -	int dma_ch = 0, ret = 0, i; +	struct dma_slave_config cfg; +	struct dma_async_tx_descriptor *tx; +	int ret = 0, i;  	struct mmc_data *data = req->data; +	struct dma_chan *chan;  	/* Sanity check: all the SG entries must be aligned by block size. */  	for (i = 0; i < data->sg_len; i++) { @@ -1356,22 +1301,41 @@ static int omap_hsmmc_start_dma_transfer(struct omap_hsmmc_host *host,  	BUG_ON(host->dma_ch != -1); -	ret = omap_request_dma(omap_hsmmc_get_dma_sync_dev(host, data), -			       "MMC/SD", omap_hsmmc_dma_cb, host, &dma_ch); -	if (ret != 0) { -		dev_err(mmc_dev(host->mmc), -			"%s: omap_request_dma() failed with %d\n", -			mmc_hostname(host->mmc), ret); +	chan = omap_hsmmc_get_dma_chan(host, data); + +	cfg.src_addr = host->mapbase + OMAP_HSMMC_DATA; +	cfg.dst_addr = host->mapbase + OMAP_HSMMC_DATA; +	cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; +	cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; +	cfg.src_maxburst = data->blksz / 4; +	cfg.dst_maxburst = data->blksz / 4; + +	ret = dmaengine_slave_config(chan, &cfg); +	if (ret)  		return ret; -	} -	ret = omap_hsmmc_pre_dma_transfer(host, data, NULL); + +	ret = omap_hsmmc_pre_dma_transfer(host, data, NULL, chan);  	if (ret)  		return ret; -	host->dma_ch = dma_ch; -	host->dma_sg_idx = 0; +	tx = dmaengine_prep_slave_sg(chan, data->sg, data->sg_len, +		data->flags & MMC_DATA_WRITE ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM, +		DMA_PREP_INTERRUPT | DMA_CTRL_ACK); +	if (!tx) { +		dev_err(mmc_dev(host->mmc), "prep_slave_sg() failed\n"); +		/* FIXME: cleanup */ +		return -1; +	} + +	tx->callback = omap_hsmmc_dma_callback; +	tx->callback_param = host; + +	/* Does not fail */ +	dmaengine_submit(tx); + +	host->dma_ch = 1; -	omap_hsmmc_config_dma_params(host, data, data->sg); +	dma_async_issue_pending(chan);  	return 0;  } @@ -1453,11 +1417,11 @@ static void omap_hsmmc_post_req(struct mmc_host *mmc, struct mmc_request *mrq,  	struct omap_hsmmc_host *host = mmc_priv(mmc);  	struct mmc_data *data = mrq->data; -	if (host->use_dma) { -		if (data->host_cookie) -			dma_unmap_sg(mmc_dev(host->mmc), data->sg, -				     data->sg_len, -				     omap_hsmmc_get_dma_dir(host, data)); +	if (host->use_dma && data->host_cookie) { +		struct dma_chan *c = omap_hsmmc_get_dma_chan(host, data); + +		dma_unmap_sg(c->device->dev, data->sg, data->sg_len, +			     omap_hsmmc_get_dma_dir(host, data));  		data->host_cookie = 0;  	}  } @@ -1472,10 +1436,13 @@ static void omap_hsmmc_pre_req(struct mmc_host *mmc, struct mmc_request *mrq,  		return ;  	} -	if (host->use_dma) +	if (host->use_dma) { +		struct dma_chan *c = omap_hsmmc_get_dma_chan(host, mrq->data); +  		if (omap_hsmmc_pre_dma_transfer(host, mrq->data, -						&host->next_data)) +						&host->next_data, c))  			mrq->data->host_cookie = 0; +	}  }  /* @@ -1808,6 +1775,8 @@ static int __devinit omap_hsmmc_probe(struct platform_device *pdev)  	struct resource *res;  	int ret, irq;  	const struct of_device_id *match; +	dma_cap_mask_t mask; +	unsigned tx_req, rx_req;  	match = of_match_device(of_match_ptr(omap_mmc_of_match), &pdev->dev);  	if (match) { @@ -1852,14 +1821,12 @@ static int __devinit omap_hsmmc_probe(struct platform_device *pdev)  	host->pdata	= pdata;  	host->dev	= &pdev->dev;  	host->use_dma	= 1; -	host->dev->dma_mask = &pdata->dma_mask;  	host->dma_ch	= -1;  	host->irq	= irq;  	host->slot_id	= 0;  	host->mapbase	= res->start + pdata->reg_offset;  	host->base	= ioremap(host->mapbase, SZ_4K);  	host->power_mode = MMC_POWER_OFF; -	host->flags	= AUTO_CMD12;  	host->next_data.cookie = 1;  	platform_set_drvdata(pdev, host); @@ -1908,7 +1875,7 @@ static int __devinit omap_hsmmc_probe(struct platform_device *pdev)  	if (IS_ERR(host->dbclk)) {  		dev_warn(mmc_dev(host->mmc), "Failed to get debounce clk\n");  		host->dbclk = NULL; -	} else if (clk_enable(host->dbclk) != 0) { +	} else if (clk_prepare_enable(host->dbclk) != 0) {  		dev_warn(mmc_dev(host->mmc), "Failed to enable debounce clk\n");  		clk_put(host->dbclk);  		host->dbclk = NULL; @@ -1940,16 +1907,35 @@ static int __devinit omap_hsmmc_probe(struct platform_device *pdev)  	res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "tx");  	if (!res) {  		dev_err(mmc_dev(host->mmc), "cannot get DMA TX channel\n"); +		ret = -ENXIO;  		goto err_irq;  	} -	host->dma_line_tx = res->start; +	tx_req = res->start;  	res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "rx");  	if (!res) {  		dev_err(mmc_dev(host->mmc), "cannot get DMA RX channel\n"); +		ret = -ENXIO; +		goto err_irq; +	} +	rx_req = res->start; + +	dma_cap_zero(mask); +	dma_cap_set(DMA_SLAVE, mask); + +	host->rx_chan = dma_request_channel(mask, omap_dma_filter_fn, &rx_req); +	if (!host->rx_chan) { +		dev_err(mmc_dev(host->mmc), "unable to obtain RX DMA engine channel %u\n", rx_req); +		ret = -ENXIO; +		goto err_irq; +	} + +	host->tx_chan = dma_request_channel(mask, omap_dma_filter_fn, &tx_req); +	if (!host->tx_chan) { +		dev_err(mmc_dev(host->mmc), "unable to obtain TX DMA engine channel %u\n", tx_req); +		ret = -ENXIO;  		goto err_irq;  	} -	host->dma_line_rx = res->start;  	/* Request IRQ for MMC operations */  	ret = request_irq(host->irq, omap_hsmmc_irq, 0, @@ -2028,11 +2014,15 @@ err_reg:  err_irq_cd_init:  	free_irq(host->irq, host);  err_irq: +	if (host->tx_chan) +		dma_release_channel(host->tx_chan); +	if (host->rx_chan) +		dma_release_channel(host->rx_chan);  	pm_runtime_put_sync(host->dev);  	pm_runtime_disable(host->dev);  	clk_put(host->fclk);  	if (host->dbclk) { -		clk_disable(host->dbclk); +		clk_disable_unprepare(host->dbclk);  		clk_put(host->dbclk);  	}  err1: @@ -2063,11 +2053,16 @@ static int __devexit omap_hsmmc_remove(struct platform_device *pdev)  	if (mmc_slot(host).card_detect_irq)  		free_irq(mmc_slot(host).card_detect_irq, host); +	if (host->tx_chan) +		dma_release_channel(host->tx_chan); +	if (host->rx_chan) +		dma_release_channel(host->rx_chan); +  	pm_runtime_put_sync(host->dev);  	pm_runtime_disable(host->dev);  	clk_put(host->fclk);  	if (host->dbclk) { -		clk_disable(host->dbclk); +		clk_disable_unprepare(host->dbclk);  		clk_put(host->dbclk);  	} @@ -2125,7 +2120,7 @@ static int omap_hsmmc_suspend(struct device *dev)  	}  	if (host->dbclk) -		clk_disable(host->dbclk); +		clk_disable_unprepare(host->dbclk);  err:  	pm_runtime_put_sync(host->dev);  	return ret; @@ -2146,7 +2141,7 @@ static int omap_hsmmc_resume(struct device *dev)  	pm_runtime_get_sync(host->dev);  	if (host->dbclk) -		clk_enable(host->dbclk); +		clk_prepare_enable(host->dbclk);  	if (!(host->mmc->pm_flags & MMC_PM_KEEP_POWER))  		omap_hsmmc_conf_bus_power(host);  | 
