diff options
| -rw-r--r-- | drivers/spi/spi-omap2-mcspi.c | 181 | 
1 files changed, 150 insertions, 31 deletions
diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index 0c73dd4f43a..9fdb7a9ae03 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -20,6 +20,8 @@   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA   *   */ +#define USE_DMA_ENGINE_RX +#define USE_DMA_ENGINE_TX  #include <linux/kernel.h>  #include <linux/init.h> @@ -28,6 +30,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> @@ -93,6 +97,8 @@  /* We have 2 DMA channels per CS, one for RX and one for TX */  struct omap2_mcspi_dma { +	struct dma_chan *dma_tx; +	struct dma_chan *dma_rx;  	int dma_tx_channel;  	int dma_rx_channel; @@ -300,6 +306,30 @@ 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)  { @@ -314,6 +344,9 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer)  	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,6 +354,71 @@ 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; @@ -342,7 +440,7 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer)  		element_count = count >> 2;  	} -	if (tx != NULL) { +	if (tx != NULL && mcspi_dma->dma_tx_channel != -1) {  		omap_set_dma_transfer_params(mcspi_dma->dma_tx_channel,  				data_type, element_count, 1,  				OMAP_DMA_SYNC_ELEMENT, @@ -357,7 +455,7 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer)  				xfer->tx_dma, 0, 0);  	} -	if (rx != NULL) { +	if (rx != NULL && mcspi_dma->dma_rx_channel != -1) {  		elements = element_count - 1;  		if (l & OMAP2_MCSPI_CHCONF_TURBO)  			elements--; @@ -377,12 +475,18 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer)  	}  	if (tx != NULL) { -		omap_start_dma(mcspi_dma->dma_tx_channel); +		if (mcspi_dma->dma_tx) +			dma_async_issue_pending(mcspi_dma->dma_tx); +		else +			omap_start_dma(mcspi_dma->dma_tx_channel);  		omap2_mcspi_set_dma_req(spi, 0, 1);  	}  	if (rx != NULL) { -		omap_start_dma(mcspi_dma->dma_rx_channel); +		if (mcspi_dma->dma_rx) +			dma_async_issue_pending(mcspi_dma->dma_rx); +		else +			omap_start_dma(mcspi_dma->dma_rx_channel);  		omap2_mcspi_set_dma_req(spi, 1, 1);  	} @@ -406,7 +510,10 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer)  		dma_unmap_single(&spi->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)) { @@ -725,32 +832,12 @@ static int omap2_mcspi_setup_transfer(struct spi_device *spi,  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); +	omap2_mcspi_rx_callback(data);  }  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); +	omap2_mcspi_tx_callback(data);  }  static int omap2_mcspi_request_dma(struct spi_device *spi) @@ -758,17 +845,43 @@ 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; +	init_completion(&mcspi_dma->dma_rx_completion); +	init_completion(&mcspi_dma->dma_tx_completion); + +	dma_cap_zero(mask); +	dma_cap_set(DMA_SLAVE, mask); +#ifdef USE_DMA_ENGINE_RX +	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; +	} +#else  	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");  		return -EAGAIN;  	} +#endif +#ifdef USE_DMA_ENGINE_TX +	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; +	} +#else  	if (omap_request_dma(mcspi_dma->dma_tx_sync_dev, "McSPI TX",  			omap2_mcspi_dma_tx_callback, spi,  			&mcspi_dma->dma_tx_channel)) { @@ -777,9 +890,7 @@ static int omap2_mcspi_request_dma(struct spi_device *spi)  		dev_err(&spi->dev, "no TX DMA channel for McSPI\n");  		return -EAGAIN;  	} - -	init_completion(&mcspi_dma->dma_rx_completion); -	init_completion(&mcspi_dma->dma_tx_completion); +#endif  	return 0;  } @@ -812,8 +923,8 @@ 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_rx_channel == -1) || +	    (!mcspi_dma->dma_tx && mcspi_dma->dma_tx_channel == -1)) {  		ret = omap2_mcspi_request_dma(spi);  		if (ret < 0)  			return ret; @@ -848,6 +959,14 @@ 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) { +			dma_release_channel(mcspi_dma->dma_rx); +			mcspi_dma->dma_rx = NULL; +		} +		if (mcspi_dma->dma_tx) { +			dma_release_channel(mcspi_dma->dma_tx); +			mcspi_dma->dma_tx = NULL; +		}  		if (mcspi_dma->dma_rx_channel != -1) {  			omap_free_dma(mcspi_dma->dma_rx_channel);  			mcspi_dma->dma_rx_channel = -1;  |