diff options
Diffstat (limited to 'drivers/dma/mxs-dma.c')
-rw-r--r-- | drivers/dma/mxs-dma.c | 111 |
1 files changed, 49 insertions, 62 deletions
diff --git a/drivers/dma/mxs-dma.c b/drivers/dma/mxs-dma.c index fc903c0ed234..c81ef7e10e08 100644 --- a/drivers/dma/mxs-dma.c +++ b/drivers/dma/mxs-dma.c @@ -22,12 +22,14 @@ #include <linux/platform_device.h> #include <linux/dmaengine.h> #include <linux/delay.h> +#include <linux/fsl/mxs-dma.h> #include <asm/irq.h> #include <mach/mxs.h> -#include <mach/dma.h> #include <mach/common.h> +#include "dmaengine.h" + /* * NOTE: The term "PIO" throughout the mxs-dma implementation means * PIO mode of mxs apbh-dma and apbx-dma. With this working mode, @@ -44,7 +46,6 @@ #define HW_APBHX_CTRL0 0x000 #define BM_APBH_CTRL0_APB_BURST8_EN (1 << 29) #define BM_APBH_CTRL0_APB_BURST_EN (1 << 28) -#define BP_APBH_CTRL0_CLKGATE_CHANNEL 8 #define BP_APBH_CTRL0_RESET_CHANNEL 16 #define HW_APBHX_CTRL1 0x010 #define HW_APBHX_CTRL2 0x020 @@ -111,7 +112,7 @@ struct mxs_dma_chan { int chan_irq; struct mxs_dma_ccw *ccw; dma_addr_t ccw_phys; - dma_cookie_t last_completed; + int desc_count; enum dma_status status; unsigned int flags; #define MXS_DMA_SG_LOOP (1 << 0) @@ -130,23 +131,6 @@ struct mxs_dma_engine { struct mxs_dma_chan mxs_chans[MXS_DMA_CHANNELS]; }; -static inline void mxs_dma_clkgate(struct mxs_dma_chan *mxs_chan, int enable) -{ - struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma; - int chan_id = mxs_chan->chan.chan_id; - int set_clr = enable ? MXS_CLR_ADDR : MXS_SET_ADDR; - - /* enable apbh channel clock */ - if (dma_is_apbh()) { - if (apbh_is_old()) - writel(1 << (chan_id + BP_APBH_CTRL0_CLKGATE_CHANNEL), - mxs_dma->base + HW_APBHX_CTRL0 + set_clr); - else - writel(1 << chan_id, - mxs_dma->base + HW_APBHX_CTRL0 + set_clr); - } -} - static void mxs_dma_reset_chan(struct mxs_dma_chan *mxs_chan) { struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma; @@ -165,9 +149,6 @@ static void mxs_dma_enable_chan(struct mxs_dma_chan *mxs_chan) struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma; int chan_id = mxs_chan->chan.chan_id; - /* clkgate needs to be enabled before writing other registers */ - mxs_dma_clkgate(mxs_chan, 1); - /* set cmd_addr up */ writel(mxs_chan->ccw_phys, mxs_dma->base + HW_APBHX_CHn_NXTCMDAR(chan_id)); @@ -178,9 +159,6 @@ static void mxs_dma_enable_chan(struct mxs_dma_chan *mxs_chan) static void mxs_dma_disable_chan(struct mxs_dma_chan *mxs_chan) { - /* disable apbh channel clock */ - mxs_dma_clkgate(mxs_chan, 0); - mxs_chan->status = DMA_SUCCESS; } @@ -216,19 +194,6 @@ static void mxs_dma_resume_chan(struct mxs_dma_chan *mxs_chan) mxs_chan->status = DMA_IN_PROGRESS; } -static dma_cookie_t mxs_dma_assign_cookie(struct mxs_dma_chan *mxs_chan) -{ - dma_cookie_t cookie = mxs_chan->chan.cookie; - - if (++cookie < 0) - cookie = 1; - - mxs_chan->chan.cookie = cookie; - mxs_chan->desc.cookie = cookie; - - return cookie; -} - static struct mxs_dma_chan *to_mxs_dma_chan(struct dma_chan *chan) { return container_of(chan, struct mxs_dma_chan, chan); @@ -240,7 +205,7 @@ static dma_cookie_t mxs_dma_tx_submit(struct dma_async_tx_descriptor *tx) mxs_dma_enable_chan(mxs_chan); - return mxs_dma_assign_cookie(mxs_chan); + return dma_cookie_assign(tx); } static void mxs_dma_tasklet(unsigned long data) @@ -268,7 +233,7 @@ static irqreturn_t mxs_dma_int_handler(int irq, void *dev_id) /* * When both completion and error of termination bits set at the * same time, we do not take it as an error. IOW, it only becomes - * an error we need to handler here in case of ether it's (1) an bus + * an error we need to handle here in case of either it's (1) a bus * error or (2) a termination error with no completion. */ stat2 = ((stat2 >> MXS_DMA_CHANNELS) & stat2) | /* (1) */ @@ -297,7 +262,7 @@ static irqreturn_t mxs_dma_int_handler(int irq, void *dev_id) stat1 &= ~(1 << channel); if (mxs_chan->status == DMA_SUCCESS) - mxs_chan->last_completed = mxs_chan->desc.cookie; + dma_cookie_complete(&mxs_chan->desc); /* schedule tasklet on this channel */ tasklet_schedule(&mxs_chan->tasklet); @@ -338,10 +303,7 @@ static int mxs_dma_alloc_chan_resources(struct dma_chan *chan) if (ret) goto err_clk; - /* clkgate needs to be enabled for reset to finish */ - mxs_dma_clkgate(mxs_chan, 1); mxs_dma_reset_chan(mxs_chan); - mxs_dma_clkgate(mxs_chan, 0); dma_async_tx_descriptor_init(&mxs_chan->desc, chan); mxs_chan->desc.tx_submit = mxs_dma_tx_submit; @@ -375,10 +337,32 @@ static void mxs_dma_free_chan_resources(struct dma_chan *chan) clk_disable_unprepare(mxs_dma->clk); } +/* + * How to use the flags for ->device_prep_slave_sg() : + * [1] If there is only one DMA command in the DMA chain, the code should be: + * ...... + * ->device_prep_slave_sg(DMA_CTRL_ACK); + * ...... + * [2] If there are two DMA commands in the DMA chain, the code should be + * ...... + * ->device_prep_slave_sg(0); + * ...... + * ->device_prep_slave_sg(DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + * ...... + * [3] If there are more than two DMA commands in the DMA chain, the code + * should be: + * ...... + * ->device_prep_slave_sg(0); // First + * ...... + * ->device_prep_slave_sg(DMA_PREP_INTERRUPT [| DMA_CTRL_ACK]); + * ...... + * ->device_prep_slave_sg(DMA_PREP_INTERRUPT | DMA_CTRL_ACK); // Last + * ...... + */ static struct dma_async_tx_descriptor *mxs_dma_prep_slave_sg( struct dma_chan *chan, struct scatterlist *sgl, - unsigned int sg_len, enum dma_data_direction direction, - unsigned long append) + unsigned int sg_len, enum dma_transfer_direction direction, + unsigned long flags, void *context) { struct mxs_dma_chan *mxs_chan = to_mxs_dma_chan(chan); struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma; @@ -386,7 +370,8 @@ static struct dma_async_tx_descriptor *mxs_dma_prep_slave_sg( struct scatterlist *sg; int i, j; u32 *pio; - static int idx; + bool append = flags & DMA_PREP_INTERRUPT; + int idx = append ? mxs_chan->desc_count : 0; if (mxs_chan->status == DMA_IN_PROGRESS && !append) return NULL; @@ -412,12 +397,11 @@ static struct dma_async_tx_descriptor *mxs_dma_prep_slave_sg( ccw->bits |= CCW_CHAIN; ccw->bits &= ~CCW_IRQ; ccw->bits &= ~CCW_DEC_SEM; - ccw->bits &= ~CCW_WAIT4END; } else { idx = 0; } - if (direction == DMA_NONE) { + if (direction == DMA_TRANS_NONE) { ccw = &mxs_chan->ccw[idx++]; pio = (u32 *) sgl; @@ -427,7 +411,8 @@ static struct dma_async_tx_descriptor *mxs_dma_prep_slave_sg( ccw->bits = 0; ccw->bits |= CCW_IRQ; ccw->bits |= CCW_DEC_SEM; - ccw->bits |= CCW_WAIT4END; + if (flags & DMA_CTRL_ACK) + ccw->bits |= CCW_WAIT4END; ccw->bits |= CCW_HALT_ON_TERM; ccw->bits |= CCW_TERM_FLUSH; ccw->bits |= BF_CCW(sg_len, PIO_NUM); @@ -450,7 +435,7 @@ static struct dma_async_tx_descriptor *mxs_dma_prep_slave_sg( ccw->bits |= CCW_CHAIN; ccw->bits |= CCW_HALT_ON_TERM; ccw->bits |= CCW_TERM_FLUSH; - ccw->bits |= BF_CCW(direction == DMA_FROM_DEVICE ? + ccw->bits |= BF_CCW(direction == DMA_DEV_TO_MEM ? MXS_DMA_CMD_WRITE : MXS_DMA_CMD_READ, COMMAND); @@ -458,10 +443,12 @@ static struct dma_async_tx_descriptor *mxs_dma_prep_slave_sg( ccw->bits &= ~CCW_CHAIN; ccw->bits |= CCW_IRQ; ccw->bits |= CCW_DEC_SEM; - ccw->bits |= CCW_WAIT4END; + if (flags & DMA_CTRL_ACK) + ccw->bits |= CCW_WAIT4END; } } } + mxs_chan->desc_count = idx; return &mxs_chan->desc; @@ -472,7 +459,8 @@ err_out: static struct dma_async_tx_descriptor *mxs_dma_prep_dma_cyclic( struct dma_chan *chan, dma_addr_t dma_addr, size_t buf_len, - size_t period_len, enum dma_data_direction direction) + size_t period_len, enum dma_transfer_direction direction, + void *context) { struct mxs_dma_chan *mxs_chan = to_mxs_dma_chan(chan); struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma; @@ -515,7 +503,7 @@ static struct dma_async_tx_descriptor *mxs_dma_prep_dma_cyclic( ccw->bits |= CCW_IRQ; ccw->bits |= CCW_HALT_ON_TERM; ccw->bits |= CCW_TERM_FLUSH; - ccw->bits |= BF_CCW(direction == DMA_FROM_DEVICE ? + ccw->bits |= BF_CCW(direction == DMA_DEV_TO_MEM ? MXS_DMA_CMD_WRITE : MXS_DMA_CMD_READ, COMMAND); dma_addr += period_len; @@ -523,6 +511,7 @@ static struct dma_async_tx_descriptor *mxs_dma_prep_dma_cyclic( i++; } + mxs_chan->desc_count = i; return &mxs_chan->desc; @@ -539,8 +528,8 @@ static int mxs_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, switch (cmd) { case DMA_TERMINATE_ALL: - mxs_dma_disable_chan(mxs_chan); mxs_dma_reset_chan(mxs_chan); + mxs_dma_disable_chan(mxs_chan); break; case DMA_PAUSE: mxs_dma_pause_chan(mxs_chan); @@ -562,7 +551,7 @@ static enum dma_status mxs_dma_tx_status(struct dma_chan *chan, dma_cookie_t last_used; last_used = chan->cookie; - dma_set_tx_state(txstate, mxs_chan->last_completed, last_used, 0); + dma_set_tx_state(txstate, chan->completed_cookie, last_used, 0); return mxs_chan->status; } @@ -580,7 +569,7 @@ static int __init mxs_dma_init(struct mxs_dma_engine *mxs_dma) ret = clk_prepare_enable(mxs_dma->clk); if (ret) - goto err_out; + return ret; ret = mxs_reset_block(mxs_dma->base); if (ret) @@ -604,11 +593,8 @@ static int __init mxs_dma_init(struct mxs_dma_engine *mxs_dma) writel(MXS_DMA_CHANNELS_MASK << MXS_DMA_CHANNELS, mxs_dma->base + HW_APBHX_CTRL1 + MXS_SET_ADDR); - clk_disable_unprepare(mxs_dma->clk); - - return 0; - err_out: + clk_disable_unprepare(mxs_dma->clk); return ret; } @@ -657,6 +643,7 @@ static int __init mxs_dma_probe(struct platform_device *pdev) mxs_chan->mxs_dma = mxs_dma; mxs_chan->chan.device = &mxs_dma->dma_device; + dma_cookie_init(&mxs_chan->chan); tasklet_init(&mxs_chan->tasklet, mxs_dma_tasklet, (unsigned long) mxs_chan); |