/*
 * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
 */

/*
 * The code contained herein is licensed under the GNU General Public
 * License. You may obtain a copy of the GNU General Public License
 * Version 2 or later at the following locations:
 *
 * http://www.opensource.org/licenses/gpl-license.html
 * http://www.gnu.org/copyleft/gpl.html
 */

/*  Front-end to the DMA handling.  This handles the allocation/freeing
 *  of DMA channels, and provides a unified interface to the machines
 *  DMA facilities.
 */

/*!
 * @file plat-mxc/dma_mx2.c
 * @brief This file contains functions for DMA  API
 *
 * @ingroup DMA_MX27
 */

#include <linux/module.h>
#include <linux/slab.h>
#include <linux/sched.h>
#include <linux/mman.h>
#include <linux/init.h>
#include <linux/spinlock.h>
#include <linux/interrupt.h>
#include <linux/clk.h>

#include <linux/proc_fs.h>
#include <asm/io.h>

#include <asm/hardware.h>
#include <asm/arch/dma.h>
#include <asm/delay.h>

#include <asm/atomic.h>

/* commented temperily for mx27 compilation
#define DMA_PM
*/
#ifdef DMA_PM
#include <linux/pm.h>
#include <asm/arch/apmc.h>
struct apmc_user *dma_apmc_user;
struct pm_dev *dma_pm;
#define DMA_PMST_RESUME		0
#define DMA_PMST_STANDBY		1
#define DMA_PMST_SUSPEND		2
static unsigned int dma_pm_status = DMA_PMST_RESUME;
#endif

/*!
 * This variable is used to controll the clock of DMA.
 * It counts the number of actived channels
 */
static atomic_t g_dma_actived = ATOMIC_INIT(0);

/*!
 * This variable point a proc file which contains the information
 *	of DMA channels
 */
static struct proc_dir_entry *g_proc_dir;

/*!
 * The dma channels
 */
static mxc_dma_channel_t g_dma_channels[MAX_DMA_CHANNELS];
static mx2_dma_priv_t g_dma_privates[MXC_DMA_CHANNELS];
static mx2_dma_bd_t g_dma_bd_table[MXC_DMA_CHANNELS][MAX_BD_SIZE];

static DEFINE_SPINLOCK(dma_list_lock);

static struct clk *dma_clk;

/*!@brief flush buffer descriptor ring*/
#define flush_dma_bd(private) \
		{ \
			atomic_set(&(private->bd_used), 0); \
			private->bd_rd = private->bd_wr;\
		}

/*!@brief get next buffer discriptor */
#define next_dma_bd(private) \
		({ \
			int bd_next = (private->bd_rd+1)%MAX_BD_SIZE; \
			(bd_next ==  private->bd_wr) ? NULL: private->bd_ring+bd_next;\
		})

static inline int consume_dma_bd(mxc_dma_channel_t * dma, int error);
/*!
 *@brief allocate a dma channel.
 *
 *@param idx   Requested channel NO.
 *                @li MXC_INVLAID_CHANNEL     System allocates a free channel which is not statically allocated.
 *                @li Others     User requests a specific channel
 *@return        @li MXC_INVLAID_CHANNEL Failure
 *               @li Others      Success
 */
static inline int get_dma_channel(int idx)
{
	int i;
	mxc_dma_channel_t *p;

	if ((idx >= MAX_DMA_CHANNELS) && (idx != MXC_DMA_DYNAMIC_CHANNEL)) {
		return -1;
	}
	if (idx != MXC_DMA_DYNAMIC_CHANNEL) {
		p = g_dma_channels + idx;
		BUG_ON(p->dynamic != 0);
		if (xchg(&p->lock, 1) != 0) {
			return -1;
		}
		return idx;
	}

	p = g_dma_channels;
	for (i = 0; (i < MAX_DMA_CHANNELS); i++, p++) {
		if (p->dynamic && (xchg(&p->lock, 1) == 0)) {
			return i;
		}
	}
	return -1;
}

/*!
 *@brief release a dma channel.
 *
 *@param idx   channel number
 *@return        none;
 */
static inline void put_dma_channel(int idx)
{
	mxc_dma_channel_t *p;

	if ((idx < MAX_DMA_CHANNELS) && (idx >= 0)) {
		p = g_dma_channels + idx;
		(void)xchg(&p->lock, 0);
	}
}

/*!
 *@brief Get dma list for /proc/dma
 */
static int mxc_get_dma_list(char *buf)
{
	mxc_dma_channel_t *dma;
	char *p = buf;
	int i;

	for (i = 0, dma = g_dma_channels; i < MAX_DMA_CHANNELS; i++, dma++) {
		if (dma->lock) {
			p += sprintf(p, "dma channel %2d: %s\n", i,
				     dma->dev_name ? dma->dev_name : "unknown");
		} else {
			p += sprintf(p, "dma channel %2d: unused\n", i);
		}
	}

	return p - buf;
}

/*!@brief save the mask of dma interrupts*/
#define save_dma_interrupt(flags) \
		flags = __raw_readl(IO_ADDRESS(DMA_BASE_ADDR) + DMA_DIMR)

/*!@brief restore the mask of dma interrupts*/
#define restore_dma_interrupt(flags) \
		__raw_writel(flags, IO_ADDRESS(DMA_BASE_ADDR) + DMA_DIMR)

/*!@brief disable interrupt of dma channel*/
static inline void mask_dma_interrupt(int channel)
{
	unsigned long reg;
	save_dma_interrupt(reg);
	reg |= 1 << channel;	/*mask interrupt; */
	restore_dma_interrupt(reg);
}

/*!@brief enable interrupt of dma channel */
static inline void unmask_dma_interrupt(int channel)
{
	unsigned long reg;
	save_dma_interrupt(reg);
	reg &= ~(1 << channel);	/*unmask interrupt; */
	restore_dma_interrupt(reg);
}

/*!@brief get interrupt event of dma channel */
static unsigned long inline __get_dma_interrupt(int channel)
{
	unsigned long mode;
	mode = 0;
	if (__raw_readl(IO_ADDRESS(DMA_BASE_ADDR) + DMA_DISR) & (1 << channel))
		mode |= DMA_DONE;

	if (__raw_readl(IO_ADDRESS(DMA_BASE_ADDR) + DMA_DBTOSR) &
	    (1 << channel))
		mode |= DMA_BURST_TIMEOUT;
	if (__raw_readl(IO_ADDRESS(DMA_BASE_ADDR) + DMA_DSESR) & (1 << channel))
		mode |= DMA_TRANSFER_ERROR;

	if (__raw_readl(IO_ADDRESS(DMA_BASE_ADDR) + DMA_DBOSR) & (1 << channel))
		mode |= DMA_BUFFER_OVERFLOW;
	if (__raw_readl(IO_ADDRESS(DMA_BASE_ADDR) + DMA_DRTOSR) &
	    (1 << channel))
		mode |= DMA_REQUEST_TIMEOUT;
	return mode;
}

/*!
 *@brief clean all event of dma interrupt and return the valid event.
 */
static unsigned long inline __clear_dma_interrupt(int channel)
{
	unsigned long mode;
	mode = __get_dma_interrupt(channel);
	__raw_writel(1 << channel, IO_ADDRESS(DMA_BASE_ADDR) + DMA_DISR);
	__raw_writel(1 << channel, IO_ADDRESS(DMA_BASE_ADDR) + DMA_DBTOSR);
	__raw_writel(1 << channel, IO_ADDRESS(DMA_BASE_ADDR) + DMA_DRTOSR);
	__raw_writel(1 << channel, IO_ADDRESS(DMA_BASE_ADDR) + DMA_DSESR);
	__raw_writel(1 << channel, IO_ADDRESS(DMA_BASE_ADDR) + DMA_DBOSR);

	return mode;
}

/*!@brief This function enables dma clocks without lock */
static void inline __enable_dma_clk(void)
{
	unsigned long reg;
	clk_enable(dma_clk);
	reg = __raw_readl(IO_ADDRESS(DMA_BASE_ADDR) + DMA_DCR);
	reg |= 0x1;
	__raw_writel(reg, IO_ADDRESS(DMA_BASE_ADDR) + DMA_DCR);
}

/*!@brief This function disables dma clocks without lock */
static void inline __disable_dma_clk(void)
{
	unsigned long reg;
	reg = __raw_readl(IO_ADDRESS(DMA_BASE_ADDR) + DMA_DCR);
	reg &= ~0x1;
	__raw_writel(reg, IO_ADDRESS(DMA_BASE_ADDR) + DMA_DCR);
	clk_disable(dma_clk);
}

/*!@brief This function enables dma clocks with lock */
static void inline enable_dma_clk(void)
{
	unsigned long flags;
	spin_lock_irqsave(&dma_list_lock, flags);
	if (atomic_read(&g_dma_actived) == 0) {
		__enable_dma_clk();
	}
	spin_unlock_irqrestore(&dma_list_lock, flags);
	return;
}

/*!@brief This function disables dma clocks without locked */
static void inline disable_dma_clk(void)
{
	unsigned long flags;
	spin_lock_irqsave(&dma_list_lock, flags);
	if (atomic_read(&g_dma_actived) == 0) {
		__disable_dma_clk();
	}
	spin_unlock_irqrestore(&dma_list_lock, flags);
	return;
}

/*!@brief select a buffer to transfer and
 * 	setup dma channel for current transfer
 */
static void setup_dmac(mxc_dma_channel_t * dma)
{
	mx2_dma_priv_t *priv = (mx2_dma_priv_t *) dma->private;
	dma_regs_t *dma_base = (dma_regs_t *) (priv->dma_base);
	mx2_dma_bd_t *p, *q;
	unsigned long ctrl_val;

	if (dma->active == 0) {
		printk(KERN_ERR
		       "dma channel %d is not enabled, when receiving this channel 's interrupt\n",
		       dma->channel);
		return;
	}
	if (atomic_read(&(priv->bd_used)) <= 0) {
		printk(KERN_ERR "dma channel %d is empty\n", dma->channel);
		dma->active = 0;
		atomic_dec(&g_dma_actived);
		return;
	}
	/* BUSY: transfering
	 * PEND: Wait for set to DMAC.
	 * s1: no transfering:
	 *      set first(one BUSY). if there are more than one tranfer. set second &repeat is enabled(two BUSY).
	 *
	 * s2: transfering & just on transfer
	 *      one BUSY. set the tranesfer and set repeat bit(two BUSY)
	 * s3: transfering & repeat has set
	 *     has two BUSY.
	 */
	p = priv->bd_ring + priv->bd_rd;
	q = next_dma_bd(priv);
	if (!(p->state & DMA_BD_ST_BUSY)) {
		/*NOTICE:: This is first buffer or dma chain does not support chain-buffer. So CEN must clear & set again */
		ctrl_val =
		    __raw_readl(&(dma_base->Ctl)) &
		    (~(DMA_CTL_ACRPT | DMA_CTL_RPT | DMA_CTL_CEN));
		__raw_writel(ctrl_val, &(dma_base->Ctl));
		if (p->mode != dma->mode) {
			dma->mode = p->mode;	/* bi-dir channel do mode change */
			if (dma->mode == MXC_DMA_MODE_READ) {
				DMA_CTL_SET_SMOD(ctrl_val,
						 priv->dma_info->sourceType);
				DMA_CTL_SET_SSIZ(ctrl_val,
						 priv->dma_info->sourcePort);
				DMA_CTL_SET_DMOD(ctrl_val,
						 priv->dma_info->destType);
				DMA_CTL_SET_DSIZ(ctrl_val,
						 priv->dma_info->destPort);
			} else {
				DMA_CTL_SET_SMOD(ctrl_val,
						 priv->dma_info->destType);
				DMA_CTL_SET_SSIZ(ctrl_val,
						 priv->dma_info->destPort);
				DMA_CTL_SET_DMOD(ctrl_val,
						 priv->dma_info->sourceType);
				DMA_CTL_SET_DSIZ(ctrl_val,
						 priv->dma_info->sourcePort);
			}
		}
		__raw_writel(p->src_addr, &(dma_base->SourceAddr));
		__raw_writel(p->dst_addr, &(dma_base->DestAddr));
		__raw_writel(p->count, &(dma_base->Count));
		p->state |= DMA_BD_ST_BUSY;
		p->state &= ~(DMA_BD_ST_PEND);
		ctrl_val |= DMA_CTL_CEN;
		__raw_writel(ctrl_val, &(dma_base->Ctl));
		if (q && priv->dma_chaining) {	/*DO chain-buffer */
			__raw_writel(q->src_addr, &(dma_base->SourceAddr));
			__raw_writel(q->dst_addr, &(dma_base->DestAddr));
			__raw_writel(q->count, &(dma_base->Count));
			q->state |= DMA_BD_ST_BUSY;
			q->state &= ~(DMA_BD_ST_PEND);
			ctrl_val |= DMA_CTL_ACRPT | DMA_CTL_RPT | DMA_CTL_CEN;
			__raw_writel(ctrl_val, &(dma_base->Ctl));
		}
	} else {		/* Just dma channel which supports dma buffer can run to there */
		BUG_ON(!priv->dma_chaining);
		if (q) {	/* p is tranfering, then q must be set into dma controller */
			/*WARNING:: [1] dangerous area begin.
			 *      If the p is completed during MCU run in this erea, the dma channel is crashed.
			 */
			__raw_writel(q->src_addr, &(dma_base->SourceAddr));
			__raw_writel(q->dst_addr, &(dma_base->DestAddr));
			__raw_writel(q->count, &(dma_base->Count));
			/*WARNING:: [2] dangerous area end */
			ctrl_val =
			    __raw_readl(&(dma_base->Ctl)) | (DMA_CTL_ACRPT |
							     DMA_CTL_RPT |
							     DMA_CTL_CEN);
			__raw_writel(ctrl_val, &(dma_base->Ctl));

			/* WARNING:: This is workaround and it is dangerous:
			 *      the judgement is not safety.
			 */
			if (!__get_dma_interrupt(dma->channel)) {
				q->state |= DMA_BD_ST_BUSY;
				q->state &= ~(DMA_BD_ST_PEND);
			} else {
				/*Waiting re-enable is in ISR */
				printk(KERN_ERR
				       "Warning:: The privous transfer is completed. Maybe the chain buffer is stopped.");
			}
		} else {	/* Last buffer is transfering: just clear RPT bit */
			ctrl_val =
			    __raw_readl(&(dma_base->Ctl)) &
			    (~(DMA_CTL_ACRPT | DMA_CTL_RPT));
			__raw_writel(ctrl_val, &(dma_base->Ctl));
		}
	}
}

/*!
 * @brief interrupt handler of dma channel
 */
static irqreturn_t dma_irq_handler(int irq, void *dev_id)
{
	mxc_dma_channel_t *dma = (mxc_dma_channel_t *) dev_id;
	mx2_dma_priv_t *priv = (mx2_dma_priv_t *) (dma ? dma->private : NULL);
	dma_regs_t *dma_base;
	int state, error = MXC_DMA_DONE;

	BUG_ON(priv == NULL);

	dma_base = (dma_regs_t *) priv->dma_base;

	state = __clear_dma_interrupt(dma->channel);

	priv->trans_bytes += dma_base->transferd;
	if (state != DMA_DONE) {
		if (state & DMA_REQUEST_TIMEOUT) {
			error = MXC_DMA_REQUEST_TIMEOUT;
		} else {
			error = MXC_DMA_TRANSFER_ERROR;
		}
	}
	if (consume_dma_bd(dma, error)) {
		disable_dma_clk();
		if (dma->cb_fn) {
			dma->cb_fn(dma->cb_args, error, priv->trans_bytes);
		}
		priv->trans_bytes = 0;
	} else {
		disable_dma_clk();
	}
	return IRQ_HANDLED;
}

/*!
 *@brief	Set DMA channel parameters
 *
 *@param	dma  Requested channel NO.
 *@param	dma_info  Channel configuration
 *@return	@li 0        	Success
 *           	@li others 	Failure
 */
static int setup_dma_channel(mxc_dma_channel_t * dma, mx2_dma_info_t * dma_info)
{
	mx2_dma_priv_t *priv = (mx2_dma_priv_t *) (dma ? dma->private : NULL);
	dma_regs_t *dma_base;
	unsigned long reg;

	if (!dma_info || !priv) {
		return -1;
	}

	if (dma_info->sourceType > 3) {
		return -1;
	}
	if (dma_info->destType > 3) {
		return -1;
	}
	if (dma_info->destPort > 3) {
		return -1;
	}
	if (dma_info->sourcePort > 3) {
		return -1;
	}
	if (dma_info->M2D_Valid) {
		/*add for second dma */
		if (dma_info->W < dma_info->X) {
			return -1;
		}
	}

	priv->dma_chaining = dma_info->dma_chaining;
	priv->ren = dma_info->ren;

	if (dma_info->sourceType != DMA_TYPE_FIFO
	    && dma_info->destType != DMA_TYPE_FIFO) {
		if (dma_info->ren) {
			printk(KERN_INFO
			       "Warning:request enable just affect source or destination port is FIFO !\n");
			priv->ren = 0;
		}
	}

	if (dma_info->M2D_Valid) {
		if (dma_info->msel) {
			__raw_writel(dma_info->W,
				     IO_ADDRESS(DMA_BASE_ADDR) + DMA_WSRB);
			__raw_writel(dma_info->X,
				     IO_ADDRESS(DMA_BASE_ADDR) + DMA_XSRB);
			__raw_writel(dma_info->Y,
				     IO_ADDRESS(DMA_BASE_ADDR) + DMA_YSRB);

		} else {
			__raw_writel(dma_info->W,
				     IO_ADDRESS(DMA_BASE_ADDR) + DMA_WSRA);
			__raw_writel(dma_info->X,
				     IO_ADDRESS(DMA_BASE_ADDR) + DMA_XSRA);
			__raw_writel(dma_info->Y,
				     IO_ADDRESS(DMA_BASE_ADDR) + DMA_YSRA);
		}
	}

	dma_base = (dma_regs_t *) (priv->dma_base);

	__raw_writel(dma_info->burstLength, &(dma_base->BurstLength));
	__raw_writel(dma_info->request, &(dma_base->RequestSource));

	if (dma_info->ren) {
		reg = dma_info->busuntils & 0x1FFFF;
		if (dma_info->rto_en) {
			reg |= 0xE000;
		}
		__raw_writel(reg, &(dma_base->BusUtilt));
	} else {
		__raw_writel(dma_info->busuntils, &(dma_base->BusUtilt));
	}

	reg = __raw_readl(&(dma_base->Ctl)) & (~(DMA_CTL_ACRPT | DMA_CTL_RPT));

	if (dma_info->dir) {
		reg |= DMA_CTL_MDIR;
	} else {
		reg &= ~DMA_CTL_MDIR;
	}

	if (priv->ren) {
		reg |= DMA_CTL_REN;
	} else {
		reg &= ~DMA_CTL_REN;
	}

	if ((dma_info->M2D_Valid) && (dma_info->msel)) {
		reg |= DMA_CTL_MSEL;
	} else {
		reg &= ~DMA_CTL_MSEL;
	}

	if (dma_info->mode) {
		DMA_CTL_SET_SMOD(reg, dma_info->destType);
		DMA_CTL_SET_SSIZ(reg, dma_info->destPort);
		DMA_CTL_SET_DMOD(reg, dma_info->sourceType);
		DMA_CTL_SET_DSIZ(reg, dma_info->sourcePort);
	} else {
		DMA_CTL_SET_SMOD(reg, dma_info->sourceType);
		DMA_CTL_SET_SSIZ(reg, dma_info->sourcePort);
		DMA_CTL_SET_DMOD(reg, dma_info->destType);
		DMA_CTL_SET_DSIZ(reg, dma_info->destPort);
	}

	__raw_writel(reg, &(dma_base->Ctl));

	__clear_dma_interrupt(dma->channel);
	unmask_dma_interrupt(dma->channel);

	disable_dma_clk();
	return 0;
}

/*!@brief setup interrupt and setup dma channel by dma parameter  */
static inline int __init_dma_channel(mxc_dma_channel_t * chan,
				     mx2_dma_info_t * dma_info)
{
	mx2_dma_priv_t *dma_private = (mx2_dma_priv_t *) chan->private;
	dma_regs_t *dma_base;
	int ret;

	mask_dma_interrupt(chan->channel);
	ret =
	    request_irq(dma_private->dma_irq, dma_irq_handler,
			IRQF_DISABLED | IRQF_SHARED, chan->dev_name,
			(void *)chan);
	if (ret) {
		printk(KERN_ERR
		       "%s: unable to request IRQ %d for DMA channel\n",
		       chan->dev_name, dma_private->dma_irq);
		return ret;
	}

	enable_dma_clk();

	dma_base = (dma_regs_t *) (dma_private->dma_base);
	__raw_writel(0, &(dma_base->Ctl));

	ret = 0;
	if ((ret = setup_dma_channel(chan, dma_info))) {
		free_irq(dma_private->dma_irq, (void *)chan);
	}
	disable_dma_clk();
	return 0;
}

/*!@brief initialize buffer descriptor ring.*/
static inline void init_dma_bd(mx2_dma_priv_t * private)
{
	int i;
	mx2_dma_bd_t *pbd;
	private->bd_rd = private->bd_wr = 0;
	atomic_set(&(private->bd_used), 0);
	for (i = 0, pbd = private->bd_ring; i < MAX_BD_SIZE; i++, pbd++) {
		pbd->state = 0;
	}
}

/*!@brief add dma buffer into buffer descriptor ring */
static inline int fill_dma_bd(mxc_dma_channel_t * dma,
			      mxc_dma_requestbuf_t * buf, int num,
			      mxc_dma_mode_t mode)
{
	int i, wr;
	unsigned long flags, mask;
	mx2_dma_priv_t *priv = dma->private;
	mx2_dma_bd_t *p, *q;

	if ((atomic_read(&(priv->bd_used)) + num) > MAX_BD_SIZE) {
		return -EBUSY;
	}

	for (i = 0; i < num; i++) {
		wr = priv->bd_wr;
		p = priv->bd_ring + wr;
		p->mode = mode;
		p->count = buf[i].num_of_bytes;
		p->src_addr = buf[i].src_addr;
		p->dst_addr = buf[i].dst_addr;
		if (i == num - 1) {
			p->state = DMA_BD_ST_LAST | DMA_BD_ST_PEND;
		} else {
			p->state = DMA_BD_ST_PEND;
		}
		priv->bd_wr = (wr + 1) % MAX_BD_SIZE;
		atomic_inc(&(priv->bd_used));

		if (atomic_read(&(priv->bd_used)) != 2)
			continue;
		/* Disable interrupt of this channel */
		local_irq_save(flags);
		local_irq_disable();
		save_dma_interrupt(mask);
		mask_dma_interrupt(dma->channel);
		local_irq_restore(flags);
		/*TODO ::
		 *  If channel is transfering and supports chain_buffer,
		 *  when the new buffer is 2st buffer , repeat must be enabled
		 */
		if (priv->dma_chaining && dma->active) {
			q = priv->bd_ring + priv->bd_rd;
			if (q && (q->state & DMA_BD_ST_BUSY)) {
				if (atomic_read(&(priv->bd_used)) == 2) {
					setup_dmac(dma);
				}
			}
		}
		restore_dma_interrupt(mask);
	}
	return 0;
}

/*!@brief add sg-list into buffer descriptor ring */
static inline int fill_dma_bd_by_sg(mxc_dma_channel_t * dma,
				    struct scatterlist *sg, int num,
				    int real_bytes, mxc_dma_mode_t mode)
{
	int i, wr, total_bytes = real_bytes;
	unsigned long flags, mask;
	mx2_dma_priv_t *priv = dma->private;
	mx2_dma_bd_t *p, *q;
	if ((atomic_read(&(priv->bd_used)) + num) > MAX_BD_SIZE) {
		return -EBUSY;
	}

	for (i = 0; i < num && ((real_bytes <= 0) || (total_bytes > 0)); i++) {
		wr = priv->bd_wr;
		p = priv->bd_ring + wr;
		p->mode = mode;
		if (real_bytes > 0) {
			if (sg[i].length >= total_bytes) {
				p->count = total_bytes;
			} else {
				p->count = sg[i].length;
			}
			total_bytes -= p->count;
		} else {
			p->count = sg[i].length;
		}
		if (mode == MXC_DMA_MODE_READ) {
			p->src_addr = priv->dma_info->per_address;
			p->dst_addr = sg[i].dma_address;
		} else {
			p->dst_addr = priv->dma_info->per_address;
			p->src_addr = sg[i].dma_address;
		}
		if ((i == num - 1) || ((real_bytes > 0) && (total_bytes == 0))) {
			p->state = DMA_BD_ST_LAST | DMA_BD_ST_PEND;
		} else {
			p->state = DMA_BD_ST_PEND;
		}
		priv->bd_wr = (wr + 1) % MAX_BD_SIZE;
		atomic_inc(&(priv->bd_used));

		if (atomic_read(&(priv->bd_used)) != 2)
			continue;
		/* Disable interrupt of this channel */
		local_irq_save(flags);
		local_irq_disable();
		save_dma_interrupt(mask);
		mask_dma_interrupt(dma->channel);
		local_irq_restore(flags);
		/*TODO ::
		 *  If channel is transfering and supports chain_buffer,
		 *  when the new buffer is 2st buffer , repeat must be enabled
		 */
		if (priv->dma_chaining && dma->active) {
			q = next_dma_bd(priv);
			if (q && (q->state & DMA_BD_ST_BUSY)) {
				if ((atomic_read(&(priv->bd_used))) == 2) {
					setup_dmac(dma);
				}
			}
		}
		restore_dma_interrupt(mask);
	}
	return 0;
}

/*!@brief select next buffer descripter to transfer.
 *	return 1: need call call-back function. 0: Not need call call-back.
 *	it just is called in ISR
 */
static inline int consume_dma_bd(mxc_dma_channel_t * dma, int error)
{
	mx2_dma_priv_t *priv = dma->private;
	mx2_dma_bd_t *p;
	int notify = 0;
	if (priv == NULL) {
		printk(KERN_ERR
		       "request dma channel %d which is not initialize completed.!\n",
		       dma->channel);
		return 1;
	}
	if (error != MXC_DMA_DONE) {
		for (p = priv->bd_ring + priv->bd_rd;
		     atomic_read(&(priv->bd_used)) > 0;) {
			priv->bd_rd = (priv->bd_rd + 1) % MAX_BD_SIZE;
			atomic_dec(&(priv->bd_used));
			if (p->state & DMA_BD_ST_LAST) {
				p->state = 0;
				break;
			}
			p->state = 0;
		}
		notify = 1;
	} else {
		p = priv->bd_ring + priv->bd_rd;
		priv->bd_rd = (priv->bd_rd + 1) % MAX_BD_SIZE;
		atomic_dec(&(priv->bd_used));
		notify = (p->state & DMA_BD_ST_LAST) == DMA_BD_ST_LAST;
	}
	if (atomic_read(&(priv->bd_used)) <= 0) {
		dma->active = 0;
		atomic_dec(&g_dma_actived);
	} else {
		setup_dmac(dma);
	}
	return notify;
}

/*!
 * This function is generally called by the driver at open time.
 * The DMA driver would do any initialization steps that is required
 * to get the channel ready for data transfer.
 *
 * @param channel_id   a pre-defined id. The peripheral driver would specify
 *                     the id associated with its peripheral. This would be
 *                     used by the DMA driver to identify the peripheral
 *                     requesting DMA and do the necessary setup on the
 *                     channel associated with the particular peripheral.
 *                     The DMA driver could use static or dynamic DMA channel
 *                     allocation.
 * @param dev_name     module name or device name
 * @return returns a negative number on error if request for a DMA channel did not
 *         succeed, returns the channel number to be used on success.
 */
int mxc_dma_request(mxc_dma_device_t channel_id, char *dev_name)
{
	mxc_dma_channel_t *dma;
	mx2_dma_priv_t *dma_private = NULL;
	mx2_dma_info_t *dma_info = mxc_dma_get_info(channel_id);
	int index;
	int ret;

	if (dma_info == NULL) {
		return -EINVAL;
	}

	if ((index = get_dma_channel(dma_info->dma_chan)) < 0) {
		return -ENODEV;
	}

	dma = g_dma_channels + index;
	dma_private = (mx2_dma_priv_t *) dma->private;
	if (dma_private == NULL) {
		printk(KERN_ERR
		       "request dma channel %d which is not initialize completed.!\n",
		       index);
		ret = -EFAULT;
		goto exit;
	}

	dma->active = 0;
	dma_private->dma_info = NULL;
	dma->cb_fn = NULL;
	dma->cb_args = NULL;
	dma->dev_name = dev_name;
	dma->mode = dma_info->mode ? MXC_DMA_MODE_WRITE : MXC_DMA_MODE_READ;
	init_dma_bd(dma_private);

	if (!(ret = __init_dma_channel(dma, dma_info))) {
		dma_private->dma_info = dma_info;
		return index;
	}
      exit:
	put_dma_channel(index);
	return ret;
}

/*!
 * This function is generally called by the driver at close time. The DMA
 * driver would do any cleanup associated with this channel.
 *
 * @param channel_num  the channel number returned at request time. This
 *                     would be used by the DMA driver to identify the calling
 *                     driver and do the necessary cleanup on the channel
 *                     associated with the particular peripheral
 * @return returns a negative number on error or 0 on success
 */
int mxc_dma_free(int channel_num)
{
	mxc_dma_channel_t *dma;
	mx2_dma_priv_t *dma_private;

	if ((channel_num >= MAX_DMA_CHANNELS) || (channel_num < 0)) {
		return -EINVAL;
	}

	dma = g_dma_channels + channel_num;
	dma_private = (mx2_dma_priv_t *) dma->private;
	if (dma_private == NULL) {
		printk(KERN_ERR
		       "Free dma %d which is not completed initialization \n",
		       channel_num);
		return -EFAULT;
	}
	if (dma->lock) {
		if (dma->active) {	/*Channel is busy */
			mxc_dma_disable(channel_num);
		}

		dma_private = (mx2_dma_priv_t *) dma->private;

		enable_dma_clk();
		mask_dma_interrupt(channel_num);
		disable_dma_clk();

		free_irq(dma_private->dma_irq, (void *)dma);
		put_dma_channel(channel_num);
	}
	return 0;
}

/*!
 * This function would just configure the buffers specified by the user into
 * dma channel. The caller must call mxc_dma_enable to start this transfer.
 *
 * @param channel_num  the channel number returned at request time. This
 *                     would be used by the DMA driver to identify the calling
 *                     driver and do the necessary cleanup on the channel
 *                     associated with the particular peripheral
 * @param dma_buf      an array of physical addresses to the user defined
 *                     buffers. The caller must guarantee the dma_buf is
 *                     available until the transfer is completed.
 * @param num_buf      number of buffers in the array
 * @param mode         specifies whether this is READ or WRITE operation
 * @return This function returns a negative number on error if buffer could not be
 *         added with DMA for transfer. On Success, it returns 0
 */
int mxc_dma_config(int channel_num, mxc_dma_requestbuf_t * dma_buf, int num_buf,
		   mxc_dma_mode_t mode)
{
	mxc_dma_channel_t *dma;
	mx2_dma_priv_t *dma_private;

	if ((dma_buf == NULL) || (num_buf < 1)) {
		return -EINVAL;
	}

	if ((channel_num >= MAX_DMA_CHANNELS) || (channel_num < 0)) {
		return -EINVAL;
	}

	dma = g_dma_channels + channel_num;
	dma_private = (mx2_dma_priv_t *) dma->private;
	if (dma_private == NULL) {
		printk(KERN_ERR
		       "config dma %d which is not completed initialization \n",
		       channel_num);
		return -EFAULT;
	}

	if (dma->lock == 0) {
		return -ENODEV;
	}

	/*TODO: dma chainning can not support on bi-dir channel */
	if (dma_private->dma_chaining && (dma->mode != mode)) {
		return -EINVAL;
	}

	/*TODO: fill dma buffer into driver .
	 * If driver is no enought buffer to save them , it will return -EBUSY
	 */
	if (fill_dma_bd(dma, dma_buf, num_buf, mode)) {
		return -EBUSY;
	}

	return 0;
}

/*!
 * This function would just configure the scatterlist specified by the
 * user into dma channel. This is a slight variation of mxc_dma_config(),
 * it is provided for the convenience of drivers that have a scatterlist
 * passed into them. It is the calling driver's responsibility to have the
 * correct physical address filled in the "dma_address" field of the
 * scatterlist.
 *
 * @param channel_num  the channel number returned at request time. This
 *                     would be used by the DMA driver to identify the calling
 *                     driver and do the necessary cleanup on the channel
 *                     associated with the particular peripheral
 * @param sg           a scatterlist of buffers. The caller must guarantee
 *                     the dma_buf is available until the transfer is
 *                     completed.
 * @param num_buf      number of buffers in the array
 * @param num_of_bytes total number of bytes to transfer. If set to 0, this
 *                     would imply to use the length field of the scatterlist
 *                     for each DMA transfer. Else it would calculate the size
 *                     for each DMA transfer.
 * @param mode         specifies whether this is READ or WRITE operation
 * @return This function returns a negative number on error if buffer could not
 *         be added with DMA for transfer. On Success, it returns 0
 */
int mxc_dma_sg_config(int channel_num, struct scatterlist *sg,
		      int num_buf, int num_of_bytes, mxc_dma_mode_t mode)
{
	mxc_dma_channel_t *dma;
	mx2_dma_priv_t *dma_private;

	if ((sg == NULL) || (num_buf < 1) || (num_of_bytes < 0)) {
		return -EINVAL;
	}

	if ((channel_num >= MAX_DMA_CHANNELS) || (channel_num < 0)) {
		return -EINVAL;
	}

	dma = g_dma_channels + channel_num;
	dma_private = (mx2_dma_priv_t *) dma->private;
	if (dma_private == NULL) {
		printk(KERN_ERR
		       "config_sg dma %d which is not completed initialization \n",
		       channel_num);
		return -EFAULT;
	}

	if (dma->lock == 0) {
		return -ENODEV;
	}

	/*TODO: dma chainning can not support on bi-dir channel */
	if (dma_private->dma_chaining && (dma->mode != mode)) {
		return -EINVAL;
	}

	/*TODO: fill dma buffer into driver .
	 * If driver is no enought buffer to save them , it will return -EBUSY
	 */
	if (fill_dma_bd_by_sg(dma, sg, num_buf, num_of_bytes, mode)) {
		return -EBUSY;
	}
	return 0;
}

/*!
 * This function is provided if the driver would like to set/change its
 * callback function.
 *
 * @param channel_num  the channel number returned at request time. This
 *                     would be used by the DMA driver to identify the calling
 *                     driver and do the necessary cleanup on the channel
 *                     associated with the particular peripheral
 * @param callback     a callback function to provide notification on transfer
 *                     completion, user could specify NULL if he does not wish
 *                     to be notified
 * @param arg          an argument that gets passed in to the callback
 *                     function, used by the user to do any driver specific
 *                     operations.
 * @return this function returns an error if the callback could not be set
 *         for the channel
 */
int mxc_dma_callback_set(int channel_num, mxc_dma_callback_t callback,
			 void *arg)
{
	mxc_dma_channel_t *dma;

	if ((channel_num >= MAX_DMA_CHANNELS) || (channel_num < 0)) {
		return -EINVAL;
	}
	dma = g_dma_channels + channel_num;

	if (!dma->lock) {
		return -ENODEV;
	}

	if (dma->active) {
		return -EBUSY;
	}
	dma->cb_fn = callback;
	dma->cb_args = arg;
	return 0;

}

/*!
 * This stops the DMA channel and any ongoing transfers. Subsequent use of
 * mxc_dma_enable() will restart the channel and restart the transfer.
 *
 * @param channel_num  the channel number returned at request time. This
 *                     would be used by the DMA driver to identify the calling
 *                     driver and do the necessary cleanup on the channel
 *                     associated with the particular peripheral
 * @return returns a negative number on error or 0 on success
 */
int mxc_dma_disable(int channel_num)
{
	mxc_dma_channel_t *dma;
	mx2_dma_priv_t *priv;
	unsigned long ctrl_val;

	if ((channel_num >= MAX_DMA_CHANNELS) || (channel_num < 0)) {
		return -EINVAL;
	}

	dma = g_dma_channels + channel_num;

	if (dma->lock == 0) {
		return -EINVAL;
	}

	if (!dma->active) {
		return -EINVAL;
	}

	priv = (mx2_dma_priv_t *) dma->private;
	if (priv == NULL) {
		printk(KERN_ERR "disable a uncompleted dma channel %d\n",
		       channel_num);
		return -EFAULT;
	}

	dma->active = 0;
	enable_dma_clk();

	__clear_dma_interrupt(channel_num);
	ctrl_val =
	    __raw_readl(IO_ADDRESS(DMA_BASE_ADDR) + DMA_CCR(channel_num));
	ctrl_val &= ~DMA_CTL_CEN;	/* clear CEN bit */
	__raw_writel(ctrl_val,
		     IO_ADDRESS(DMA_BASE_ADDR) + DMA_CCR(channel_num));
	disable_dma_clk();
	atomic_dec(&g_dma_actived);

	/*TODO: Clear all request buffers */
	flush_dma_bd(priv);
	return 0;
}

/*!
 * This starts DMA transfer. Or it restarts DMA on a stopped channel
 * previously stopped with mxc_dma_disable().
 *
 * @param channel_num  the channel number returned at request time. This
 *                     would be used by the DMA driver to identify the calling
 *                     driver and do the necessary cleanup on the channel
 *                     associated with the particular peripheral
 * @return returns a negative number on error or 0 on success
 */
int mxc_dma_enable(int channel_num)
{
	mxc_dma_channel_t *dma;
	mx2_dma_priv_t *priv;

	if ((channel_num >= MAX_DMA_CHANNELS) || (channel_num < 0)) {
		return -EINVAL;
	}

	dma = g_dma_channels + channel_num;

	if (dma->lock == 0) {
		return -EINVAL;
	}

	priv = (mx2_dma_priv_t *) dma->private;
	if (priv == NULL) {
		printk(KERN_ERR "enable a uncompleted dma channel %d\n",
		       channel_num);
		return -EFAULT;
	}

	if (dma->active) {
		return 0;
	}
	dma->active = 1;
	priv->trans_bytes = 0;

	enable_dma_clk();

	atomic_inc(&g_dma_actived);
	__clear_dma_interrupt(channel_num);

	setup_dmac(dma);
	disable_dma_clk();
	return 0;
}

/*!
*@brief Dump DMA registers
*
*@param channel  Requested channel NO.
*@return     none
*/

void mxc_dump_dma_register(int channel)
{
	mxc_dma_channel_t *dma = &g_dma_channels[channel];
	mx2_dma_priv_t *priv = (mx2_dma_priv_t *) dma->private;
	dma_regs_t *dma_base;

	printk(KERN_INFO "======== Dump dma channel %d \n", channel);
	if ((unsigned)channel >= MXC_DMA_CHANNELS) {
		printk(KERN_INFO "Channel number is invalid \n");
		return;
	}
	if (!dma->lock) {
		printk(KERN_INFO "Channel is not allocated \n");
		return;
	}

	printk(KERN_INFO "g_dma_actived = %d\n", atomic_read(&g_dma_actived));

	enable_dma_clk();
	dma_base = (dma_regs_t *) (priv->dma_base);
	printk(KERN_INFO "DMA COMMON REGISTER\n");
	printk(KERN_INFO "DMA CONTROL             DMA_DCR: %08x\n",
	       __raw_readl(IO_ADDRESS(DMA_BASE_ADDR) + DMA_DCR));
	printk(KERN_INFO "DMA Interrupt status    DMA_DISR: %08x\n",
	       __raw_readl(IO_ADDRESS(DMA_BASE_ADDR) + DMA_DISR));
	printk(KERN_INFO "DMA Interrupt Mask      DMA_DIMR: %08x\n",
	       __raw_readl(IO_ADDRESS(DMA_BASE_ADDR) + DMA_DIMR));
	printk(KERN_INFO "DMA Burst Time Out      DMA_DBTOSR: %08x\n",
	       __raw_readl(IO_ADDRESS(DMA_BASE_ADDR) + DMA_DBTOSR));
	printk(KERN_INFO "DMA request Time Out    DMA_DRTOSR: %08x\n",
	       __raw_readl(IO_ADDRESS(DMA_BASE_ADDR) + DMA_DRTOSR));
	printk(KERN_INFO "DMA Transfer Error      DMA_DSESR: %08x\n",
	       __raw_readl(IO_ADDRESS(DMA_BASE_ADDR) + DMA_DSESR));
	printk(KERN_INFO "DMA DMA_Overflow        DMA_DBOSR: %08x\n",
	       __raw_readl(IO_ADDRESS(DMA_BASE_ADDR) + DMA_DBOSR));
	printk(KERN_INFO "DMA Burst Time OutCtl   DMA_BurstTOCtl: %08x\n",
	       __raw_readl(IO_ADDRESS(DMA_BASE_ADDR) + DMA_DBTOCR));

	printk(KERN_INFO "DMA 2D X size: %08x\n",
	       __raw_readl(IO_ADDRESS(DMA_BASE_ADDR) + DMA_XSRA));
	printk(KERN_INFO "DMA 2D Y size: %08x\n",
	       __raw_readl(IO_ADDRESS(DMA_BASE_ADDR) + DMA_YSRA));
	printk(KERN_INFO "DMA 2D Z size: %08x\n",
	       __raw_readl(IO_ADDRESS(DMA_BASE_ADDR) + DMA_WSRA));

	printk(KERN_INFO "DMA Chan %2d  Sourc     SourceAddr: %08x\n", channel,
	       __raw_readl(&(dma_base->SourceAddr)));
	printk(KERN_INFO "DMA Chan %2d  dest      DestAddr: %08x\n", channel,
	       __raw_readl(&(dma_base->DestAddr)));
	printk(KERN_INFO "DMA Chan %2d  count     Count: %08x\n", channel,
	       __raw_readl(&(dma_base->Count)));
	printk(KERN_INFO "DMA Chan %2d  Ctl       Ctl: %08x\n", channel,
	       __raw_readl(&(dma_base->Ctl)));
	printk(KERN_INFO "DMA Chan %2d  request   RequestSource: %08x\n",
	       channel, __raw_readl(&(dma_base->RequestSource)));
	printk(KERN_INFO "DMA Chan %2d  burstL    BurstLength: %08x\n", channel,
	       __raw_readl(&(dma_base->BurstLength)));
	printk(KERN_INFO "DMA Chan %2d  requestTO ReqTimeout: %08x\n", channel,
	       __raw_readl(&(dma_base->ReqTimeout)));
	printk(KERN_INFO "DMA Chan %2d  BusUtilt  BusUtilt: %08x\n", channel,
	       __raw_readl(&(dma_base->BusUtilt)));

	disable_dma_clk();
}

#ifdef DMA_PM

static int channel_in_use(void)
{
	int i;
	for (i = 0; i < MXC_DMA_CHANNELS; i++) {
		if (dma_chan[i].lock)
			return 1;
	}
	return 0;
}

int mxc_dma_pm_standby(void)
{
	unsigned long reg;
	if (dma_pm_status == DMA_PMST_STANDBY)
		return 0;

	if (!channel_in_use()) {
		/*Disable DMA */
		__disable_dma_clk();
		dma_pm_status = DMA_PMST_STANDBY;
		return 0;
	}
	return -1;
}

int mxc_dma_pm_resume(void)
{
	unsigned long reg;
	if (dma_pm_status == DMA_PMST_RESUME)
		return 0;

	/*Enable HCLK_DMA and DMA(ipg clock) */
	dma_pm_status = DMA_PMST_RESUME;
	return 0;
}

int mxc_dma_pm_suspend(void)
{
	unsigned long reg;
	if (dma_pm_status == DMA_PMST_SUSPEND)
		return 0;

	if (!channel_in_use()) {
		/*Disable DMA */
		__disable_dma_clk();
		dma_pm_status = DMA_PMST_SUSPEND;
		return 0;
	}
	return -1;
}

int mxc_dma_pm_handler(struct pm_dev *dev, pm_request_t rqst, void *data)
{
	int ret = 0;
	switch (rqst) {
		/*APM doesn't send PM_STANDBY and PM_STANDBY_RESUME request now. */
	case PM_SUSPEND:
		ret = dma_pm_suspend();
		break;
	case PM_RESUME:
		ret = dma_pm_resume();
		break;
	}
	return ret;
}

#endif				/*DMA_PM */

int __init mxc_dma_init(void)
{
	int i;
	mxc_dma_channel_t *dma = g_dma_channels;
	mx2_dma_priv_t *private = g_dma_privates;

	memset(dma, 0, sizeof(mxc_dma_channel_t) * MXC_DMA_CHANNELS);
	for (i = 0; i < MXC_DMA_CHANNELS; i++, dma++, private++) {
		dma->channel = i;
		dma->private = private;
		private->dma_base =
		    (unsigned int)(IO_ADDRESS(DMA_BASE_ADDR + DMA_CH_BASE(i)));
		private->dma_irq = i + MXC_DMA_INTR_0;	/*Dma channel interrupt number */
		private->bd_ring = &g_dma_bd_table[i][0];
	}

	mxc_dma_load_info(g_dma_channels);

	dma_clk = clk_get(NULL, "dma_clk");
	clk_enable(dma_clk);

	__raw_writel(0x2, IO_ADDRESS(DMA_BASE_ADDR) + DMA_DCR);	/*reset DMA; */

	disable_dma_clk();

	/*use module init because create_proc after init_dma */
	g_proc_dir = create_proc_entry("dma", 0, NULL);
	g_proc_dir->read_proc = (read_proc_t *) mxc_get_dma_list;
	g_proc_dir->data = NULL;

#ifdef DMA_PM
	/* Register the device with power management. */
	dma_pm = pm_register(PM_DMA_DEV, PM_SYS_UNKNOWN, dma_pm_handler);
#endif

	return 0;
}

arch_initcall(mxc_dma_init);

EXPORT_SYMBOL(mxc_dma_request);
EXPORT_SYMBOL(mxc_dma_free);
EXPORT_SYMBOL(mxc_dma_callback_set);
EXPORT_SYMBOL(mxc_dma_enable);
EXPORT_SYMBOL(mxc_dma_disable);
EXPORT_SYMBOL(mxc_dma_config);
EXPORT_SYMBOL(mxc_dma_sg_config);
EXPORT_SYMBOL(mxc_dump_dma_register);
