diff options
Diffstat (limited to 'drivers/spi/spi-omap2-mcspi.c')
| -rw-r--r-- | drivers/spi/spi-omap2-mcspi.c | 237 | 
1 files changed, 132 insertions, 105 deletions
diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index 0c73dd4f43a0..bc4778175e34 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -28,6 +28,8 @@  #include <linux/device.h>  #include <linux/delay.h>  #include <linux/dma-mapping.h> +#include <linux/dmaengine.h> +#include <linux/omap-dma.h>  #include <linux/platform_device.h>  #include <linux/err.h>  #include <linux/clk.h> @@ -39,7 +41,6 @@  #include <linux/spi/spi.h> -#include <plat/dma.h>  #include <plat/clock.h>  #include <plat/mcspi.h> @@ -93,8 +94,8 @@  /* We have 2 DMA channels per CS, one for RX and one for TX */  struct omap2_mcspi_dma { -	int dma_tx_channel; -	int dma_rx_channel; +	struct dma_chan *dma_tx; +	struct dma_chan *dma_rx;  	int dma_tx_sync_dev;  	int dma_rx_sync_dev; @@ -300,20 +301,46 @@ static int mcspi_wait_for_reg_bit(void __iomem *reg, unsigned long bit)  	return 0;  } +static void omap2_mcspi_rx_callback(void *data) +{ +	struct spi_device *spi = data; +	struct omap2_mcspi *mcspi = spi_master_get_devdata(spi->master); +	struct omap2_mcspi_dma *mcspi_dma = &mcspi->dma_channels[spi->chip_select]; + +	complete(&mcspi_dma->dma_rx_completion); + +	/* We must disable the DMA RX request */ +	omap2_mcspi_set_dma_req(spi, 1, 0); +} + +static void omap2_mcspi_tx_callback(void *data) +{ +	struct spi_device *spi = data; +	struct omap2_mcspi *mcspi = spi_master_get_devdata(spi->master); +	struct omap2_mcspi_dma *mcspi_dma = &mcspi->dma_channels[spi->chip_select]; + +	complete(&mcspi_dma->dma_tx_completion); + +	/* We must disable the DMA TX request */ +	omap2_mcspi_set_dma_req(spi, 0, 0); +} +  static unsigned  omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer)  {  	struct omap2_mcspi	*mcspi;  	struct omap2_mcspi_cs	*cs = spi->controller_state;  	struct omap2_mcspi_dma  *mcspi_dma; -	unsigned int		count, c; -	unsigned long		base, tx_reg, rx_reg; -	int			word_len, data_type, element_count; +	unsigned int		count; +	int			word_len, element_count;  	int			elements = 0;  	u32			l;  	u8			* rx;  	const u8		* tx;  	void __iomem		*chstat_reg; +	struct dma_slave_config	cfg; +	enum dma_slave_buswidth width; +	unsigned es;  	mcspi = spi_master_get_devdata(spi->master);  	mcspi_dma = &mcspi->dma_channels[spi->chip_select]; @@ -321,74 +348,99 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer)  	chstat_reg = cs->base + OMAP2_MCSPI_CHSTAT0; +	if (cs->word_len <= 8) { +		width = DMA_SLAVE_BUSWIDTH_1_BYTE; +		es = 1; +	} else if (cs->word_len <= 16) { +		width = DMA_SLAVE_BUSWIDTH_2_BYTES; +		es = 2; +	} else { +		width = DMA_SLAVE_BUSWIDTH_4_BYTES; +		es = 4; +	} + +	memset(&cfg, 0, sizeof(cfg)); +	cfg.src_addr = cs->phys + OMAP2_MCSPI_RX0; +	cfg.dst_addr = cs->phys + OMAP2_MCSPI_TX0; +	cfg.src_addr_width = width; +	cfg.dst_addr_width = width; +	cfg.src_maxburst = 1; +	cfg.dst_maxburst = 1; + +	if (xfer->tx_buf && mcspi_dma->dma_tx) { +		struct dma_async_tx_descriptor *tx; +		struct scatterlist sg; + +		dmaengine_slave_config(mcspi_dma->dma_tx, &cfg); + +		sg_init_table(&sg, 1); +		sg_dma_address(&sg) = xfer->tx_dma; +		sg_dma_len(&sg) = xfer->len; + +		tx = dmaengine_prep_slave_sg(mcspi_dma->dma_tx, &sg, 1, +			DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); +		if (tx) { +			tx->callback = omap2_mcspi_tx_callback; +			tx->callback_param = spi; +			dmaengine_submit(tx); +		} else { +			/* FIXME: fall back to PIO? */ +		} +	} + +	if (xfer->rx_buf && mcspi_dma->dma_rx) { +		struct dma_async_tx_descriptor *tx; +		struct scatterlist sg; +		size_t len = xfer->len - es; + +		dmaengine_slave_config(mcspi_dma->dma_rx, &cfg); + +		if (l & OMAP2_MCSPI_CHCONF_TURBO) +			len -= es; + +		sg_init_table(&sg, 1); +		sg_dma_address(&sg) = xfer->rx_dma; +		sg_dma_len(&sg) = len; + +		tx = dmaengine_prep_slave_sg(mcspi_dma->dma_rx, &sg, 1, +			DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); +		if (tx) { +			tx->callback = omap2_mcspi_rx_callback; +			tx->callback_param = spi; +			dmaengine_submit(tx); +		} else { +			/* FIXME: fall back to PIO? */ +		} +	} +  	count = xfer->len; -	c = count;  	word_len = cs->word_len; -	base = cs->phys; -	tx_reg = base + OMAP2_MCSPI_TX0; -	rx_reg = base + OMAP2_MCSPI_RX0;  	rx = xfer->rx_buf;  	tx = xfer->tx_buf;  	if (word_len <= 8) { -		data_type = OMAP_DMA_DATA_TYPE_S8;  		element_count = count;  	} else if (word_len <= 16) { -		data_type = OMAP_DMA_DATA_TYPE_S16;  		element_count = count >> 1;  	} else /* word_len <= 32 */ { -		data_type = OMAP_DMA_DATA_TYPE_S32;  		element_count = count >> 2;  	}  	if (tx != NULL) { -		omap_set_dma_transfer_params(mcspi_dma->dma_tx_channel, -				data_type, element_count, 1, -				OMAP_DMA_SYNC_ELEMENT, -				mcspi_dma->dma_tx_sync_dev, 0); - -		omap_set_dma_dest_params(mcspi_dma->dma_tx_channel, 0, -				OMAP_DMA_AMODE_CONSTANT, -				tx_reg, 0, 0); - -		omap_set_dma_src_params(mcspi_dma->dma_tx_channel, 0, -				OMAP_DMA_AMODE_POST_INC, -				xfer->tx_dma, 0, 0); -	} - -	if (rx != NULL) { -		elements = element_count - 1; -		if (l & OMAP2_MCSPI_CHCONF_TURBO) -			elements--; - -		omap_set_dma_transfer_params(mcspi_dma->dma_rx_channel, -				data_type, elements, 1, -				OMAP_DMA_SYNC_ELEMENT, -				mcspi_dma->dma_rx_sync_dev, 1); - -		omap_set_dma_src_params(mcspi_dma->dma_rx_channel, 0, -				OMAP_DMA_AMODE_CONSTANT, -				rx_reg, 0, 0); - -		omap_set_dma_dest_params(mcspi_dma->dma_rx_channel, 0, -				OMAP_DMA_AMODE_POST_INC, -				xfer->rx_dma, 0, 0); -	} - -	if (tx != NULL) { -		omap_start_dma(mcspi_dma->dma_tx_channel); +		dma_async_issue_pending(mcspi_dma->dma_tx);  		omap2_mcspi_set_dma_req(spi, 0, 1);  	}  	if (rx != NULL) { -		omap_start_dma(mcspi_dma->dma_rx_channel); +		dma_async_issue_pending(mcspi_dma->dma_rx);  		omap2_mcspi_set_dma_req(spi, 1, 1);  	}  	if (tx != NULL) {  		wait_for_completion(&mcspi_dma->dma_tx_completion); -		dma_unmap_single(&spi->dev, xfer->tx_dma, count, DMA_TO_DEVICE); +		dma_unmap_single(mcspi->dev, xfer->tx_dma, count, +				 DMA_TO_DEVICE);  		/* for TX_ONLY mode, be sure all words have shifted out */  		if (rx == NULL) { @@ -403,10 +455,14 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer)  	if (rx != NULL) {  		wait_for_completion(&mcspi_dma->dma_rx_completion); -		dma_unmap_single(&spi->dev, xfer->rx_dma, count, DMA_FROM_DEVICE); +		dma_unmap_single(mcspi->dev, xfer->rx_dma, count, +				 DMA_FROM_DEVICE);  		omap2_mcspi_set_enable(spi, 0); +		elements = element_count - 1; +  		if (l & OMAP2_MCSPI_CHCONF_TURBO) { +			elements--;  			if (likely(mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHSTAT0)  				   & OMAP2_MCSPI_CHSTAT_RXS)) { @@ -723,64 +779,38 @@ static int omap2_mcspi_setup_transfer(struct spi_device *spi,  	return 0;  } -static void omap2_mcspi_dma_rx_callback(int lch, u16 ch_status, void *data) -{ -	struct spi_device	*spi = data; -	struct omap2_mcspi	*mcspi; -	struct omap2_mcspi_dma	*mcspi_dma; - -	mcspi = spi_master_get_devdata(spi->master); -	mcspi_dma = &(mcspi->dma_channels[spi->chip_select]); - -	complete(&mcspi_dma->dma_rx_completion); - -	/* We must disable the DMA RX request */ -	omap2_mcspi_set_dma_req(spi, 1, 0); -} - -static void omap2_mcspi_dma_tx_callback(int lch, u16 ch_status, void *data) -{ -	struct spi_device	*spi = data; -	struct omap2_mcspi	*mcspi; -	struct omap2_mcspi_dma	*mcspi_dma; - -	mcspi = spi_master_get_devdata(spi->master); -	mcspi_dma = &(mcspi->dma_channels[spi->chip_select]); - -	complete(&mcspi_dma->dma_tx_completion); - -	/* We must disable the DMA TX request */ -	omap2_mcspi_set_dma_req(spi, 0, 0); -} -  static int omap2_mcspi_request_dma(struct spi_device *spi)  {  	struct spi_master	*master = spi->master;  	struct omap2_mcspi	*mcspi;  	struct omap2_mcspi_dma	*mcspi_dma; +	dma_cap_mask_t mask; +	unsigned sig;  	mcspi = spi_master_get_devdata(master);  	mcspi_dma = mcspi->dma_channels + spi->chip_select; -	if (omap_request_dma(mcspi_dma->dma_rx_sync_dev, "McSPI RX", -			omap2_mcspi_dma_rx_callback, spi, -			&mcspi_dma->dma_rx_channel)) { -		dev_err(&spi->dev, "no RX DMA channel for McSPI\n"); +	init_completion(&mcspi_dma->dma_rx_completion); +	init_completion(&mcspi_dma->dma_tx_completion); + +	dma_cap_zero(mask); +	dma_cap_set(DMA_SLAVE, mask); +	sig = mcspi_dma->dma_rx_sync_dev; +	mcspi_dma->dma_rx = dma_request_channel(mask, omap_dma_filter_fn, &sig); +	if (!mcspi_dma->dma_rx) { +		dev_err(&spi->dev, "no RX DMA engine channel for McSPI\n");  		return -EAGAIN;  	} -	if (omap_request_dma(mcspi_dma->dma_tx_sync_dev, "McSPI TX", -			omap2_mcspi_dma_tx_callback, spi, -			&mcspi_dma->dma_tx_channel)) { -		omap_free_dma(mcspi_dma->dma_rx_channel); -		mcspi_dma->dma_rx_channel = -1; -		dev_err(&spi->dev, "no TX DMA channel for McSPI\n"); +	sig = mcspi_dma->dma_tx_sync_dev; +	mcspi_dma->dma_tx = dma_request_channel(mask, omap_dma_filter_fn, &sig); +	if (!mcspi_dma->dma_tx) { +		dev_err(&spi->dev, "no TX DMA engine channel for McSPI\n"); +		dma_release_channel(mcspi_dma->dma_rx); +		mcspi_dma->dma_rx = NULL;  		return -EAGAIN;  	} -	init_completion(&mcspi_dma->dma_rx_completion); -	init_completion(&mcspi_dma->dma_tx_completion); -  	return 0;  } @@ -812,8 +842,7 @@ static int omap2_mcspi_setup(struct spi_device *spi)  		list_add_tail(&cs->node, &ctx->cs);  	} -	if (mcspi_dma->dma_rx_channel == -1 -			|| mcspi_dma->dma_tx_channel == -1) { +	if (!mcspi_dma->dma_rx || !mcspi_dma->dma_tx) {  		ret = omap2_mcspi_request_dma(spi);  		if (ret < 0)  			return ret; @@ -848,13 +877,13 @@ static void omap2_mcspi_cleanup(struct spi_device *spi)  	if (spi->chip_select < spi->master->num_chipselect) {  		mcspi_dma = &mcspi->dma_channels[spi->chip_select]; -		if (mcspi_dma->dma_rx_channel != -1) { -			omap_free_dma(mcspi_dma->dma_rx_channel); -			mcspi_dma->dma_rx_channel = -1; +		if (mcspi_dma->dma_rx) { +			dma_release_channel(mcspi_dma->dma_rx); +			mcspi_dma->dma_rx = NULL;  		} -		if (mcspi_dma->dma_tx_channel != -1) { -			omap_free_dma(mcspi_dma->dma_tx_channel); -			mcspi_dma->dma_tx_channel = -1; +		if (mcspi_dma->dma_tx) { +			dma_release_channel(mcspi_dma->dma_tx); +			mcspi_dma->dma_tx = NULL;  		}  	}  } @@ -1032,7 +1061,7 @@ static int omap2_mcspi_transfer_one_message(struct spi_master *master,  	return 0;  } -static int __init omap2_mcspi_master_setup(struct omap2_mcspi *mcspi) +static int __devinit omap2_mcspi_master_setup(struct omap2_mcspi *mcspi)  {  	struct spi_master	*master = mcspi->master;  	struct omap2_mcspi_regs	*ctx = &mcspi->ctx; @@ -1174,7 +1203,6 @@ static int __devinit omap2_mcspi_probe(struct platform_device *pdev)  			break;  		} -		mcspi->dma_channels[i].dma_rx_channel = -1;  		mcspi->dma_channels[i].dma_rx_sync_dev = dma_res->start;  		sprintf(dma_ch_name, "tx%d", i);  		dma_res = platform_get_resource_byname(pdev, IORESOURCE_DMA, @@ -1185,7 +1213,6 @@ static int __devinit omap2_mcspi_probe(struct platform_device *pdev)  			break;  		} -		mcspi->dma_channels[i].dma_tx_channel = -1;  		mcspi->dma_channels[i].dma_tx_sync_dev = dma_res->start;  	}  | 
