Here's a patch which converts the SPI driver to use dma engine support. It's more or less a mechanical replacement in this driver. Tested on OMAP4430SDP ethernet (which basically means that udhcpc works.) One thing to note: the old DMA code programmed the DMA hardware for an element count of N and frame size of 1, with element sync. The DMA engine code will program the DMA hardware for an element count of 1 and a frame size of N, with frame sync. This appears to work - but at the moment I've not looked to see if there's any implications of this. You can enable/disable the DMA engine code for the TX or RX side by tweaking the two definitions at the top of the file if you want to do comparisons. Comments? If none, I'll see about committing this into the patch set, adding the removal of the private DMA code, and sending it out more "officially" with the next round of RFC for this set. Modulo the element vs frame sync issue, I think folk can see that the conversion process is fairly straight forward - it's more or less: 1. add struct dma_chan * pointer to the private structure. 2. add allocation/freeing functions for the DMA engine channels. 3. convert the callbacks to match the DMA engine callback prototype. 4. add code to set the DMA engine channel configuration appropriately (avoiding any unnecessary calls) via dmaengine_slave_config(). 5. add code to queue the transfer via dmaengine_prep_slave_sg() and dmaengine_submit(). 6. omap_start_dma() becomes dma_async_issue_pending(). drivers/spi/spi-omap2-mcspi.c | 186 +++++++++++++++++++++++++++++++++------- 1 files changed, 153 insertions(+), 33 deletions(-) diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index bb9274c..a77af0b 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,7 @@ #include <linux/device.h> #include <linux/delay.h> #include <linux/dma-mapping.h> +#include <linux/dmaengine.h> #include <linux/platform_device.h> #include <linux/err.h> #include <linux/clk.h> @@ -95,6 +98,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; @@ -290,6 +295,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) { @@ -304,6 +333,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]; @@ -311,6 +343,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; @@ -332,7 +429,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, @@ -347,7 +444,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--; @@ -367,12 +464,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); } @@ -396,7 +499,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)) { @@ -715,50 +821,58 @@ 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); } +extern bool omap_dma_filter_fn(struct dma_chan *chan, void *param); + 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)) { @@ -767,9 +881,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; } @@ -803,8 +915,8 @@ static int omap2_mcspi_setup(struct spi_device *spi) &omap2_mcspi_ctx[mcspi->master->bus_num - 1].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; @@ -839,6 +951,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; -- To unsubscribe from this list: send the line "unsubscribe linux-omap" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html