summaryrefslogtreecommitdiff
path: root/target/linux/ep93xx/patches-2.6.30/005-ep93xx-dma.patch
diff options
context:
space:
mode:
Diffstat (limited to 'target/linux/ep93xx/patches-2.6.30/005-ep93xx-dma.patch')
-rw-r--r--target/linux/ep93xx/patches-2.6.30/005-ep93xx-dma.patch3622
1 files changed, 3622 insertions, 0 deletions
diff --git a/target/linux/ep93xx/patches-2.6.30/005-ep93xx-dma.patch b/target/linux/ep93xx/patches-2.6.30/005-ep93xx-dma.patch
new file mode 100644
index 0000000000..3664132021
--- /dev/null
+++ b/target/linux/ep93xx/patches-2.6.30/005-ep93xx-dma.patch
@@ -0,0 +1,3622 @@
+--- /dev/null
++++ b/arch/arm/mach-ep93xx/dma_ep93xx.c
+@@ -0,0 +1,2940 @@
++/******************************************************************************
++ * arch/arm/mach-ep9312/dma_ep93xx.c
++ *
++ * Support functions for the ep93xx internal DMA channels.
++ * (see also Documentation/arm/ep93xx/dma.txt)
++ *
++ * Copyright (C) 2003 Cirrus Logic
++ *
++ * A large portion of this file is based on the dma api implemented by
++ * Nicolas Pitre, dma-sa1100.c, copyrighted 2000.
++ *
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ *
++ ****************************************************************************/
++#include <linux/autoconf.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/sched.h>
++#include <linux/spinlock.h>
++#include <linux/slab.h>
++#include <linux/errno.h>
++#include <linux/delay.h>
++#include <linux/interrupt.h>
++
++#include <asm/system.h>
++#include <asm/irq.h>
++#include <asm/hardware.h>
++#include <asm/io.h>
++#include <asm/dma.h>
++#include <asm/mach/dma.h>
++#include "dma_ep93xx.h"
++
++/*****************************************************************************
++ *
++ * Debugging macros
++ *
++ ****************************************************************************/
++#undef DEBUG
++//#define DEBUG 1
++#ifdef DEBUG
++#define DPRINTK( fmt, arg... ) printk( fmt, ##arg )
++#else
++#define DPRINTK( fmt, arg... )
++#endif
++
++/*****************************************************************************
++ *
++ * static global variables
++ *
++ ****************************************************************************/
++ep93xx_dma_t dma_chan[MAX_EP93XX_DMA_CHANNELS];
++
++/*
++ * lock used to protect the list of dma channels while searching for a free
++ * channel during dma_request.
++ */
++//static spinlock_t dma_list_lock;
++static spinlock_t dma_list_lock = SPIN_LOCK_UNLOCKED;
++
++/*****************************************************************************
++ *
++ * Internal DMA processing functions.
++ *
++ ****************************************************************************/
++/*****************************************************************************
++ *
++ * get_dma_channel_from_handle()
++ *
++ * If Handle is valid, returns the DMA channel # (0 to 9 for channels 1-10)
++ * If Handle is not valid, returns -1.
++ *
++ ****************************************************************************/
++static int
++dma_get_channel_from_handle(int handle)
++{
++ int channel;
++
++ /*
++ * Get the DMA channel # from the handle.
++ */
++ channel = ((int)handle & DMA_HANDLE_SPECIFIER_MASK) >> 28;
++
++ /*
++ * See if this is a valid handle.
++ */
++ if (dma_chan[channel].last_valid_handle != (int)handle) {
++ DPRINTK("DMA ERROR - invalid handle 0x%x \n", handle);
++ return(-1);
++ }
++
++ /*
++ * See if this instance is still open
++ */
++ if (!dma_chan[channel].ref_count )
++ return(-1);
++
++ return(channel);
++}
++
++static void dma_m2m_transfer_done(ep93xx_dma_t *dma)
++{
++ unsigned int uiCONTROL;
++ unsigned int M2M_reg_base = dma->reg_base;
++ unsigned int read_back;
++
++ DPRINTK("1 ");
++
++ outl( 0, M2M_reg_base+M2M_OFFSET_INTERRUPT );
++
++ if (dma->total_buffers) {
++ /*
++ * The current_buffer has already been tranfered, so add the
++ * byte count to the total_bytes field.
++ */
++ dma->total_bytes = dma->total_bytes +
++ dma->buffer_queue[dma->current_buffer].size;
++
++ /*
++ * Mark the current_buffer as used.
++ */
++ dma->buffer_queue[dma->current_buffer].used = TRUE;
++
++ /*
++ * Increment the used buffer counter
++ */
++ dma->used_buffers++;
++
++ DPRINTK("#%d", dma->current_buffer);
++
++ /*
++ * Increment the current_buffer
++ */
++ dma->current_buffer = (dma->current_buffer + 1) %
++ MAX_EP93XX_DMA_BUFFERS;
++
++ /*
++ * check if there's a new buffer to transfer.
++ */
++ if (dma->new_buffers && dma->xfer_enable) {
++ /*
++ * We have a new buffer to transfer so program in the
++ * buffer values. Since a STALL interrupt was
++ * triggered, we program the buffer descriptor 0
++ *
++ * Set the SAR_BASE/DAR_BASE/BCR registers with values
++ * from the next buffer in the queue.
++ */
++ outl( dma->buffer_queue[dma->current_buffer].source,
++ M2M_reg_base + M2M_OFFSET_SAR_BASE0 );
++
++ outl( dma->buffer_queue[dma->current_buffer].dest,
++ M2M_reg_base + M2M_OFFSET_DAR_BASE0 );
++
++ outl( dma->buffer_queue[dma->current_buffer].size,
++ M2M_reg_base + M2M_OFFSET_BCR0 );
++
++ DPRINTK("SAR_BASE0 - 0x%x\n", dma->buffer_queue[dma->current_buffer].source);
++ DPRINTK("DAR_BASE0 - 0x%x\n", dma->buffer_queue[dma->current_buffer].dest);
++ DPRINTK("BCR0 - 0x%x\n", dma->buffer_queue[dma->current_buffer].size);
++
++ /*
++ * Decrement the new buffer counter
++ */
++ dma->new_buffers--;
++
++ /*
++ * If there's a second new buffer, we program the
++ * second buffer descriptor.
++ */
++ if (dma->new_buffers) {
++ outl( dma->buffer_queue[(dma->current_buffer + 1) %
++ MAX_EP93XX_DMA_BUFFERS].source,
++ M2M_reg_base+M2M_OFFSET_SAR_BASE1 );
++
++ outl( dma->buffer_queue[(dma->current_buffer + 1) %
++ MAX_EP93XX_DMA_BUFFERS].dest,
++ M2M_reg_base+M2M_OFFSET_DAR_BASE1 );
++
++ outl( dma->buffer_queue[(dma->current_buffer + 1) %
++ MAX_EP93XX_DMA_BUFFERS].size,
++ M2M_reg_base+M2M_OFFSET_BCR1 );
++
++ uiCONTROL = inl(M2M_reg_base+M2M_OFFSET_CONTROL);
++ uiCONTROL |= CONTROL_M2M_NFBINTEN;
++ outl( uiCONTROL, M2M_reg_base+M2M_OFFSET_CONTROL );
++
++ dma->new_buffers--;
++ }
++ } else {
++ DPRINTK("2 \n");
++ /*
++ * There's a chance we setup both buffer descriptors,
++ * but didn't service the NFB quickly enough, causing
++ * the channel to transfer both buffers, then enter the
++ * stall state. So, we need to be able to process the
++ * second buffer.
++ */
++ if ((dma->used_buffers + dma->new_buffers) < dma->total_buffers)
++ {
++ DPRINTK("3 ");
++
++ /*
++ * The current_buffer has already been
++ * tranferred, so add the byte count to the
++ * total_bytes field.
++ */
++ dma->total_bytes = dma->total_bytes +
++ dma->buffer_queue[dma->current_buffer].size;
++
++ /*
++ * Mark the current_buffer as used.
++ */
++ dma->buffer_queue[dma->current_buffer].used = TRUE;
++
++ /*
++ * Increment the used buffer counter
++ */
++ dma->used_buffers++;
++
++ DPRINTK("#%d", dma->current_buffer);
++
++ /*
++ * Increment the current buffer pointer.
++ */
++ dma->current_buffer = (dma->current_buffer + 1) %
++ MAX_EP93XX_DMA_BUFFERS;
++
++ }
++
++ /*
++ * No new buffers to transfer, so disable the channel.
++ */
++ uiCONTROL = inl(M2M_reg_base+M2M_OFFSET_CONTROL);
++ uiCONTROL &= ~CONTROL_M2M_ENABLE;
++ outl( uiCONTROL, M2M_reg_base+M2M_OFFSET_CONTROL );
++
++ /*
++ * Indicate that this channel is in the pause by
++ * starvation state by setting the pause bit to true.
++ */
++ dma->pause = TRUE;
++ }
++ } else {
++ /*
++ * No buffers to transfer, or old buffers to mark as used,
++ * so disable the channel
++ */
++ uiCONTROL = inl(M2M_reg_base+M2M_OFFSET_CONTROL);
++ uiCONTROL &= ~CONTROL_M2M_ENABLE;
++ outl( uiCONTROL, M2M_reg_base+M2M_OFFSET_CONTROL );
++
++ /*
++ * Must read the control register back after a write.
++ */
++ read_back = inl(M2M_reg_base+M2M_OFFSET_CONTROL);
++
++ /*
++ * Indicate that this channel is in the pause by
++ * starvation state by setting the pause bit to true.
++ */
++ dma->pause = TRUE;
++ }
++}
++
++static void dma_m2m_next_frame_buffer(ep93xx_dma_t *dma)
++{
++ int loop;
++ unsigned int uiCONTROL;
++ unsigned int M2M_reg_base = dma->reg_base;
++
++ DPRINTK("5 ");
++
++ if (dma->total_buffers) {
++ DPRINTK("6 ");
++ /*
++ * The iCurrentBuffer has already been transfered. so add the
++ * byte count from the current buffer to the total byte count.
++ */
++ dma->total_bytes = dma->total_bytes +
++ dma->buffer_queue[dma->current_buffer].size;
++
++ /*
++ * Mark the Current Buffer as used.
++ */
++ dma->buffer_queue[dma->current_buffer].used = TRUE;
++
++ /*
++ * Increment the used buffer counter
++ */
++ dma->used_buffers++;
++
++ DPRINTK("#%d", dma->current_buffer);
++
++ if ((dma->buffer_queue[
++ (dma->current_buffer + 1) % MAX_EP93XX_DMA_BUFFERS].last) ||
++ (dma->new_buffers == 0) || (dma->xfer_enable == FALSE)) {
++ DPRINTK("7 ");
++
++ /*
++ * This is the last Buffer in this transaction, so
++ * disable the NFB interrupt. We shouldn't get an NFB
++ * int when the FSM moves to the ON state where it
++ * would typically get the NFB int indicating a new
++ * buffer can be programmed. Instead, once in the ON
++ * state, the DMA will just proceed to complete the
++ * transfer of the current buffer, move the FSB
++ * directly to the STALL state where a STALL interrupt
++ * will be generated.
++ */
++ uiCONTROL = inl(M2M_reg_base+M2M_OFFSET_CONTROL);
++ uiCONTROL &= ~CONTROL_M2M_NFBINTEN ;
++ outl( uiCONTROL, M2M_reg_base+M2M_OFFSET_CONTROL );
++
++ /*
++ * The current buffer has been transferred, so
++ * increment the current buffer counter to reflect
++ * this.
++ */
++ dma->current_buffer = (dma->current_buffer + 1) %
++ MAX_EP93XX_DMA_BUFFERS;
++
++ DPRINTK("End of NFB handling. \n");
++ DPRINTK("CONTROL - 0x%x \n",
++ inl(M2M_reg_base+M2M_OFFSET_CONTROL) );
++ DPRINTK("STATUS - 0x%x \n",
++ inl(M2M_reg_base+M2M_OFFSET_STATUS) );
++ DPRINTK("SAR_BASE0 - 0x%x \n",
++ inl(M2M_reg_base+M2M_OFFSET_SAR_BASE0) );
++ DPRINTK("SAR_CUR0 - 0x%x \n",
++ inl(M2M_reg_base+M2M_OFFSET_SAR_CURRENT0) );
++ DPRINTK("DAR_BASE0 - 0x%x \n",
++ inl(M2M_reg_base+M2M_OFFSET_DAR_BASE0) );
++ DPRINTK("DAR_CUR0 - 0x%x \n",
++ inl(M2M_reg_base+M2M_OFFSET_DAR_CURRENT0) );
++
++ DPRINTK("Buffer buf_id source size last used \n");
++ for (loop = 0; loop < 32; loop ++)
++ DPRINTK("%d 0x%x 0x%x 0x%x %d %d \n",
++ loop, dma->buffer_queue[loop].buf_id,
++ dma->buffer_queue[loop].source,
++ dma->buffer_queue[loop].size,
++ dma->buffer_queue[loop].last,
++ dma->buffer_queue[loop].used);
++ DPRINTK("pause 0x%x 0x%x 0x%x %d %d \n",
++ dma->pause_buf.buf_id, dma->pause_buf.source,
++ dma->pause_buf.size, dma->pause_buf.last,
++ dma->pause_buf.used);
++
++ DPRINTK("Pause - %d \n", dma->pause);
++ DPRINTK("xfer_enable - %d \n", dma->xfer_enable);
++ DPRINTK("total bytes - 0x%x \n", dma->total_bytes);
++ DPRINTK("total buffer - %d \n", dma->total_buffers);
++ DPRINTK("new buffers - %d \n", dma->new_buffers);
++ DPRINTK("current buffer - %d \n", dma->current_buffer);
++ DPRINTK("last buffer - %d \n", dma->last_buffer);
++ DPRINTK("used buffers - %d \n", dma->used_buffers);
++ DPRINTK("callback addr - 0x%p \n", dma->callback);
++
++ } else if (dma->new_buffers) {
++ DPRINTK("8 ");
++ /*
++ * We have a new buffer, so increment the current
++ * buffer to point to the next buffer, which is already
++ * programmed into the DMA. Next time around, it'll be
++ * pointing to the current buffer.
++ */
++ dma->current_buffer = (dma->current_buffer + 1) %
++ MAX_EP93XX_DMA_BUFFERS;
++
++ /*
++ * We know we have a new buffer to program as the next
++ * buffer, so check which set of SAR_BASE/DAR_BASE/BCR
++ * registers to program.
++ */
++ if ( inl(M2M_reg_base+M2M_OFFSET_STATUS) & STATUS_M2M_NB ) {
++ /*
++ * Set the SAR_BASE1/DAR_BASE1/BCR1 registers
++ * with values from the next buffer in the
++ * queue.
++ */
++ outl( dma->buffer_queue[(dma->current_buffer + 1) %
++ MAX_EP93XX_DMA_BUFFERS].source,
++ M2M_reg_base+M2M_OFFSET_SAR_BASE1 );
++
++ outl( dma->buffer_queue[(dma->current_buffer + 1) %
++ MAX_EP93XX_DMA_BUFFERS].dest,
++ M2M_reg_base+M2M_OFFSET_DAR_BASE1 );
++
++ outl( dma->buffer_queue[(dma->current_buffer + 1) %
++ MAX_EP93XX_DMA_BUFFERS].size,
++ M2M_reg_base+M2M_OFFSET_BCR1 );
++ } else {
++ /*
++ * Set the SAR_BASE0/DAR_BASE0/BCR0 registers
++ * with values from the next buffer in the
++ * queue.
++ */
++ outl( dma->buffer_queue[(dma->current_buffer + 1) %
++ MAX_EP93XX_DMA_BUFFERS].source,
++ M2M_reg_base+M2M_OFFSET_SAR_BASE0 );
++
++ outl( dma->buffer_queue[(dma->current_buffer + 1) %
++ MAX_EP93XX_DMA_BUFFERS].dest,
++ M2M_reg_base+M2M_OFFSET_DAR_BASE0 );
++
++ outl( dma->buffer_queue[(dma->current_buffer + 1) %
++ MAX_EP93XX_DMA_BUFFERS].size,
++ M2M_reg_base+M2M_OFFSET_BCR0 );
++ }
++
++ /*
++ * Decrement the new buffers counter
++ */
++ dma->new_buffers--;
++ }
++ } else {
++ /*
++ * Total number of buffers is 0 - really we should never get
++ * here, but just in case.
++ */
++ DPRINTK("9 \n");
++
++ /*
++ * No new buffers to transfer, so Disable the channel
++ */
++ uiCONTROL = inl(M2M_reg_base+M2M_OFFSET_CONTROL);
++ uiCONTROL &= ~CONTROL_M2M_ENABLE;
++ outl( uiCONTROL, M2M_reg_base+M2M_OFFSET_CONTROL );
++
++ /*
++ * Indicate that the channel is paused by starvation.
++ */
++ dma->pause = 1;
++ }
++}
++
++/*****************************************************************************
++ *
++ * dma_m2m_irq_handler
++ *
++ ****************************************************************************/
++static irqreturn_t
++dma_m2m_irq_handler(int irq, void *dev_id)
++{
++ ep93xx_dma_t *dma = (ep93xx_dma_t *)dev_id;
++ unsigned int M2M_reg_base = dma->reg_base;
++ ep93xx_dma_dev_t dma_int = UNDEF_INT;
++ int status;
++
++// printk("+m2m irq=%d\n", irq);
++
++ /*
++ * Determine what kind of dma interrupt this is.
++ */
++ status = inl(M2M_reg_base + M2M_OFFSET_INTERRUPT);
++ if ( status & INTERRUPT_M2M_DONEINT )
++ dma_int = DONE; // we're done with a requested dma
++ else if ( status & INTERRUPT_M2M_NFBINT )
++ dma_int = NFB; // we're done with one dma buffer
++
++ DPRINTK("IRQ: b=%#x st=%#x\n", (int)dma->current_buffer, dma_int);
++
++ switch (dma_int) {
++ /*
++ * Next Frame Buffer Interrupt. If there's a new buffer program it
++ * Check if this is the last buffer in the transfer,
++ * and if it is, disable the NFB int to prevent being
++ * interrupted for another buffer when we know there won't be
++ * another.
++ */
++ case NFB:
++ dma_m2m_next_frame_buffer(dma);
++ break;
++ /*
++ * Done interrupt generated, indicating that the transfer is complete.
++ */
++ case DONE:
++ dma_m2m_transfer_done(dma);
++ break;
++
++ default:
++ break;
++ }
++
++ if ((dma_int != UNDEF_INT) && dma->callback)
++ dma->callback(dma_int, dma->device, dma->user_data);
++
++ return IRQ_HANDLED;
++}
++
++/*****************************************************************************
++ *
++ * dma_m2p_irq_handler
++ *
++ *
++ *
++ ****************************************************************************/
++static irqreturn_t
++dma_m2p_irq_handler(int irq, void *dev_id)
++{
++ ep93xx_dma_t *dma = (ep93xx_dma_t *) dev_id;
++ unsigned int M2P_reg_base = dma->reg_base;
++ unsigned int read_back;
++ ep93xx_dma_dev_t dma_int = UNDEF_INT;
++ unsigned int loop, uiCONTROL, uiINTERRUPT;
++
++ /*
++ * Determine what kind of dma interrupt this is.
++ */
++ if ( inl(M2P_reg_base+M2P_OFFSET_INTERRUPT) & INTERRUPT_M2P_STALLINT )
++ dma_int = STALL;
++ else if ( inl(M2P_reg_base+M2P_OFFSET_INTERRUPT) & INTERRUPT_M2P_NFBINT )
++ dma_int = NFB;
++ else if ( inl(M2P_reg_base+M2P_OFFSET_INTERRUPT) & INTERRUPT_M2P_CHERRORINT )
++ dma_int = CHERROR;
++
++ /*
++ * Stall Interrupt: The Channel is stalled, meaning nothing is
++ * programmed to transfer right now. So, we're back to the
++ * beginnning. If there's a buffer to transfer, program it into
++ * max and base 0 registers.
++ */
++ if (dma_int == STALL) {
++ DPRINTK("1 ");
++
++ if (dma->total_buffers) {
++ /*
++ * The current_buffer has already been tranfered, so
++ * add the byte count to the total_bytes field.
++ */
++ dma->total_bytes = dma->total_bytes +
++ dma->buffer_queue[dma->current_buffer].size;
++
++ /*
++ * Mark the current_buffer as used.
++ */
++ dma->buffer_queue[dma->current_buffer].used = TRUE;
++
++ /*
++ * Increment the used buffer counter
++ */
++ dma->used_buffers++;
++
++ DPRINTK("#%d", dma->current_buffer);
++
++ /*
++ * Increment the current_buffer
++ */
++ dma->current_buffer = (dma->current_buffer + 1) %
++ MAX_EP93XX_DMA_BUFFERS;
++
++ /*
++ * check if there's a new buffer to transfer.
++ */
++ if (dma->new_buffers && dma->xfer_enable) {
++ /*
++ * We have a new buffer to transfer so program
++ * in the buffer values. Since a STALL
++ * interrupt was triggered, we program the
++ * base0 and maxcnt0
++ *
++ * Set the MAXCNT0 register with the buffer
++ * size
++ */
++ outl( dma->buffer_queue[dma->current_buffer].size,
++ M2P_reg_base+M2P_OFFSET_MAXCNT0 );
++
++ /*
++ * Set the BASE0 register with the buffer base
++ * address
++ */
++ outl( dma->buffer_queue[dma->current_buffer].source,
++ M2P_reg_base+M2P_OFFSET_BASE0 );
++
++ /*
++ * Decrement the new buffer counter
++ */
++ dma->new_buffers--;
++
++ if (dma->new_buffers) {
++ DPRINTK("A ");
++ /*
++ * Set the MAXCNT1 register with the
++ * buffer size
++ */
++ outl( dma->buffer_queue[(dma->current_buffer + 1) %
++ MAX_EP93XX_DMA_BUFFERS].size,
++ M2P_reg_base+M2P_OFFSET_MAXCNT1 );
++
++ /*
++ * Set the BASE1 register with the
++ * buffer base address
++ */
++ outl( dma->buffer_queue[dma->current_buffer + 1 %
++ MAX_EP93XX_DMA_BUFFERS].source,
++ M2P_reg_base+M2P_OFFSET_BASE1 );
++
++ /*
++ * Decrement the new buffer counter
++ */
++ dma->new_buffers--;
++
++ /*
++ * Enable the NFB Interrupt.
++ */
++ uiCONTROL = inl(M2P_reg_base+M2P_OFFSET_CONTROL);
++ uiCONTROL |= CONTROL_M2P_NFBINTEN;
++ outl( uiCONTROL, M2P_reg_base+M2P_OFFSET_CONTROL );
++ }
++ } else {
++ /*
++ * No new buffers.
++ */
++ DPRINTK("2 \n");
++
++ /*
++ * There's a chance we setup both buffer descriptors, but
++ * didn't service the NFB quickly enough, causing the channel
++ * to transfer both buffers, then enter the stall state.
++ * So, we need to be able to process the second buffer.
++ */
++ if ((dma->used_buffers + dma->new_buffers) < dma->total_buffers) {
++ DPRINTK("3 ");
++
++ /*
++ * The current_buffer has already been tranfered, so add the
++ * byte count to the total_bytes field.
++ */
++ dma->total_bytes = dma->total_bytes +
++ dma->buffer_queue[dma->current_buffer].size;
++
++ /*
++ * Mark the current_buffer as used.
++ */
++ dma->buffer_queue[dma->current_buffer].used = TRUE;
++
++ /*
++ * Increment the used buffer counter
++ */
++ dma->used_buffers++;
++
++ DPRINTK("#%d", dma->current_buffer);
++
++ /*
++ * Increment the current buffer pointer.
++ */
++ dma->current_buffer = (dma->current_buffer + 1) %
++ MAX_EP93XX_DMA_BUFFERS;
++
++ }
++
++ /*
++ * No new buffers to transfer, so disable the channel.
++ */
++ uiCONTROL = inl(M2P_reg_base+M2P_OFFSET_CONTROL);
++ uiCONTROL &= ~CONTROL_M2P_ENABLE;
++ outl( uiCONTROL, M2P_reg_base+M2P_OFFSET_CONTROL );
++
++ /*
++ * Indicate that this channel is in the pause by starvation
++ * state by setting the pause bit to true.
++ */
++ dma->pause = TRUE;
++
++ DPRINTK("STATUS - 0x%x \n", inl(M2P_reg_base+M2P_OFFSET_STATUS) );
++ DPRINTK("CONTROL - 0x%x \n", inl(M2P_reg_base+M2P_OFFSET_CONTROL) );
++ DPRINTK("REMAIN - 0x%x \n", inl(M2P_reg_base+M2P_OFFSET_REMAIN) );
++ DPRINTK("PPALLOC - 0x%x \n", inl(M2P_reg_base+M2P_OFFSET_PPALLOC) );
++ DPRINTK("BASE0 - 0x%x \n", inl(M2P_reg_base+M2P_OFFSET_BASE0) );
++ DPRINTK("MAXCNT0 - 0x%x \n", inl(M2P_reg_base+M2P_OFFSET_MAXCNT0) );
++ DPRINTK("CURRENT0 - 0x%x \n", inl(M2P_reg_base+M2P_OFFSET_CURRENT0) );
++ DPRINTK("BASE1 - 0x%x \n", inl(M2P_reg_base+M2P_OFFSET_BASE1) );
++ DPRINTK("MAXCNT1 - 0x%x \n", inl(M2P_reg_base+M2P_OFFSET_MAXCNT1) );
++ DPRINTK("CURRENT1 - 0x%x \n", inl(M2P_reg_base+M2P_OFFSET_CURRENT1) );
++
++ DPRINTK("Buffer buf_id source size last used \n");
++ for (loop = 0; loop < 32; loop ++)
++ DPRINTK("%d 0x%x 0x%x 0x%x %d %d \n",
++ loop, dma->buffer_queue[loop].buf_id, dma->buffer_queue[loop].source,
++ dma->buffer_queue[loop].size,
++ dma->buffer_queue[loop].last, dma->buffer_queue[loop].used);
++ DPRINTK("pause 0x%x 0x%x 0x%x %d %d \n",
++ dma->pause_buf.buf_id, dma->pause_buf.source, dma->pause_buf.size,
++ dma->pause_buf.last, dma->pause_buf.used);
++
++ DPRINTK("Pause - %d \n", dma->pause);
++ DPRINTK("xfer_enable - %d \n", dma->xfer_enable);
++ DPRINTK("total bytes - 0x%x \n", dma->total_bytes);
++ DPRINTK("total buffer - %d \n", dma->total_buffers);
++ DPRINTK("new buffers - %d \n", dma->new_buffers);
++ DPRINTK("current buffer - %d \n", dma->current_buffer);
++ DPRINTK("last buffer - %d \n", dma->last_buffer);
++ DPRINTK("used buffers - %d \n", dma->used_buffers);
++ DPRINTK("callback addr - 0x%p \n", dma->callback);
++ }
++ } else {
++ /*
++ * No buffers to transfer, or old buffers to mark as used,
++ * so Disable the channel
++ */
++ uiCONTROL = inl(M2P_reg_base+M2P_OFFSET_CONTROL);
++ uiCONTROL &= ~CONTROL_M2P_ENABLE;
++ outl( uiCONTROL, M2P_reg_base+M2P_OFFSET_CONTROL );
++
++ /*
++ * Must read the control register back after a write.
++ */
++ read_back = inl(M2P_reg_base+M2P_OFFSET_CONTROL);
++
++ /*
++ * Indicate that this channel is in the pause by
++ * starvation state by setting the pause bit to true.
++ */
++ dma->pause = TRUE;
++ }
++ }
++
++ /*
++ * Next Frame Buffer Interrupt. If there's a new buffer program it
++ * Check if this is the last buffer in the transfer,
++ * and if it is, disable the NFB int to prevent being
++ * interrupted for another buffer when we know there won't be
++ * another.
++ */
++ if (dma_int == NFB) {
++ DPRINTK("5 ");
++
++ if (dma->total_buffers) {
++ DPRINTK("6 ");
++ /*
++ * The iCurrentBuffer has already been transfered. so add the
++ * byte count from the current buffer to the total byte count.
++ */
++ dma->total_bytes = dma->total_bytes +
++ dma->buffer_queue[dma->current_buffer].size;
++
++ /*
++ * Mark the Current Buffer as used.
++ */
++ dma->buffer_queue[dma->current_buffer].used = TRUE;
++
++ /*
++ * Increment the used buffer counter
++ */
++ dma->used_buffers++;
++
++ DPRINTK("#%d", dma->current_buffer);
++
++ if ((dma->buffer_queue[
++ (dma->current_buffer + 1) % MAX_EP93XX_DMA_BUFFERS].last) ||
++ (dma->new_buffers == 0) || (dma->xfer_enable == FALSE)) {
++ DPRINTK("7 ");
++
++ /*
++ * This is the last Buffer in this transaction, so disable
++ * the NFB interrupt. We shouldn't get an NFB int when the
++ * FSM moves to the ON state where it would typically get the
++ * NFB int indicating a new buffer can be programmed.
++ * Instead, once in the ON state, the DMA will just proceed
++ * to complet the transfer of the current buffer, move the
++ * FSB directly to the STALL state where a STALL interrupt
++ * will be generated.
++ */
++ uiCONTROL = inl(M2P_reg_base+M2P_OFFSET_CONTROL);
++ uiCONTROL &= ~CONTROL_M2P_NFBINTEN;
++ outl( uiCONTROL, M2P_reg_base+M2P_OFFSET_CONTROL );
++
++ /*
++ * The current buffer has been transferred, so increment
++ * the current buffer counter to reflect this.
++ */
++ dma->current_buffer = (dma->current_buffer + 1) % MAX_EP93XX_DMA_BUFFERS;
++
++ DPRINTK("End of NFB handling. \n");
++ DPRINTK("STATUS - 0x%x \n", inl(M2P_reg_base+M2P_OFFSET_STATUS) );
++ DPRINTK("CONTROL - 0x%x \n", inl(M2P_reg_base+M2P_OFFSET_CONTROL) );
++ DPRINTK("REMAIN - 0x%x \n", inl(M2P_reg_base+M2P_OFFSET_REMAIN) );
++ DPRINTK("PPALLOC - 0x%x \n", inl(M2P_reg_base+M2P_OFFSET_PPALLOC) );
++ DPRINTK("BASE0 - 0x%x \n", inl(M2P_reg_base+M2P_OFFSET_BASE0) );
++ DPRINTK("MAXCNT0 - 0x%x \n", inl(M2P_reg_base+M2P_OFFSET_MAXCNT0) );
++ DPRINTK("CURRENT0 - 0x%x \n", inl(M2P_reg_base+M2P_OFFSET_CURRENT0) );
++ DPRINTK("BASE1 - 0x%x \n", inl(M2P_reg_base+M2P_OFFSET_BASE1) );
++ DPRINTK("MAXCNT1 - 0x%x \n", inl(M2P_reg_base+M2P_OFFSET_MAXCNT1) );
++ DPRINTK("CURRENT1 - 0x%x \n", inl(M2P_reg_base+M2P_OFFSET_CURRENT1) );
++
++ DPRINTK("Buffer buf_id source size last used \n");
++ for (loop = 0; loop < 32; loop ++)
++ DPRINTK("%d 0x%x 0x%x 0x%x %d %d \n",
++ loop, dma->buffer_queue[loop].buf_id, dma->buffer_queue[loop].source,
++ dma->buffer_queue[loop].size,
++ dma->buffer_queue[loop].last, dma->buffer_queue[loop].used);
++ DPRINTK("pause 0x%x 0x%x 0x%x %d %d \n",
++ dma->pause_buf.buf_id, dma->pause_buf.source, dma->pause_buf.size,
++ dma->pause_buf.last, dma->pause_buf.used);
++
++ DPRINTK("Pause - %d \n", dma->pause);
++ DPRINTK("xfer_enable - %d \n", dma->xfer_enable);
++ DPRINTK("total bytes - 0x%x \n", dma->total_bytes);
++ DPRINTK("total buffer - %d \n", dma->total_buffers);
++ DPRINTK("new buffers - %d \n", dma->new_buffers);
++ DPRINTK("current buffer - %d \n", dma->current_buffer);
++ DPRINTK("last buffer - %d \n", dma->last_buffer);
++ DPRINTK("used buffers - %d \n", dma->used_buffers);
++ DPRINTK("callback addr - 0x%p \n", dma->callback);
++
++ } else if (dma->new_buffers) {
++ DPRINTK("8 ");
++ /*
++ * we have a new buffer, so increment the current buffer to
++ * point to the next buffer, which is already programmed into
++ * the DMA. Next time around, it'll be pointing to the
++ * current buffer.
++ */
++ dma->current_buffer = (dma->current_buffer + 1) % MAX_EP93XX_DMA_BUFFERS;
++
++ /*
++ * we know we have a new buffer to program as the next
++ * buffer, so check which set of MAXCNT and BASE registers
++ * to program.
++ */
++ if ( inl(M2P_reg_base+M2P_OFFSET_STATUS) & STATUS_M2P_NEXTBUFFER ) {
++ /*
++ * Set the MAXCNT1 register with the buffer size
++ */
++ outl( dma->buffer_queue[
++ (dma->current_buffer + 1) % MAX_EP93XX_DMA_BUFFERS].size,
++ M2P_reg_base+M2P_OFFSET_MAXCNT1 );
++
++ /*
++ * Set the BASE1 register with the buffer base address
++ */
++ outl( dma->buffer_queue[
++ (dma->current_buffer + 1) % MAX_EP93XX_DMA_BUFFERS].source,
++ M2P_reg_base+M2P_OFFSET_BASE1 );
++ } else {
++ /*
++ * Set the MAXCNT0 register with the buffer size
++ */
++ outl( dma->buffer_queue[
++ (dma->current_buffer + 1) % MAX_EP93XX_DMA_BUFFERS].size,
++ M2P_reg_base+M2P_OFFSET_MAXCNT0 );
++
++ /*
++ * Set the BASE0 register with the buffer base address
++ */
++ outl( dma->buffer_queue[
++ (dma->current_buffer + 1) % MAX_EP93XX_DMA_BUFFERS].source,
++ M2P_reg_base+M2P_OFFSET_BASE0 );
++ }
++
++ /*
++ * Decrement the new buffers counter
++ */
++ dma->new_buffers--;
++ }
++ } else {
++ /*
++ * Total number of buffers is 0 - really we should never get here,
++ * but just in case.
++ */
++ DPRINTK("9 \n");
++
++ /*
++ * No new buffers to transfer, so Disable the channel
++ */
++ uiCONTROL = inl(M2P_reg_base+M2P_OFFSET_CONTROL);
++ uiCONTROL &= ~CONTROL_M2P_ENABLE;
++ outl( uiCONTROL, M2P_reg_base+M2P_OFFSET_CONTROL );
++ }
++ }
++
++ /*
++ * Channel Error Interrupt, or perhipheral interrupt, specific to the
++ * memory to/from peripheral channels.
++ */
++ if (dma_int == CHERROR) {
++ /*
++ * just clear the interrupt, it's really up to the peripheral
++ * driver to determine if any further action is necessary.
++ */
++ uiINTERRUPT = inl(M2P_reg_base+M2P_OFFSET_INTERRUPT);
++ uiINTERRUPT &= ~INTERRUPT_M2P_CHERRORINT;
++ outl( uiINTERRUPT, M2P_reg_base+M2P_OFFSET_INTERRUPT );
++ }
++
++ /*
++ * Make sure the interrupt was valid, and if it was, then check
++ * if a callback function was installed for this DMA channel. If a
++ * callback was installed call it.
++ */
++ if ((dma_int != UNDEF_INT) && dma->callback)
++ dma->callback(dma_int, dma->device, dma->user_data);
++
++ return IRQ_HANDLED;
++}
++
++/*****************************************************************************
++ *
++ * ep9312_dma_open_m2p(int device)
++ *
++ * Description: This function will attempt to open a M2P/P2M DMA channel.
++ * If the open is successful, the channel number is returned,
++ * otherwise a negative number is returned.
++ *
++ * Parameters:
++ * device: device for which the dma channel is requested.
++ *
++ ****************************************************************************/
++static int
++dma_open_m2p(int device)
++{
++ int channel = -1;
++ unsigned int loop;
++ unsigned int M2P_reg_base;
++ unsigned int uiPWRCNT;
++ /*unsigned long flags;*/
++
++ DPRINTK("DMA Open M2P with hw dev %d\n", device);
++
++ /*
++ * Lock the dma channel list.
++ */
++ //spin_lock_irqsave(&dma_list_lock, flags);
++ spin_lock(&dma_list_lock);
++
++ /*
++ * Verify that the device requesting DMA isn't already using a DMA channel
++ */
++ if (device >= 10)
++ loop = 1; // Rx transfer requested
++ else
++ loop = 0; // Tx transfer requested
++
++ for (; loop < 10; loop = loop + 2)
++ /*
++ * Before checking for a matching device, check that the
++ * channel is in use, otherwise the device field is
++ * invalid.
++ */
++ if (dma_chan[loop].ref_count)
++ if (device == dma_chan[loop].device) {
++ DPRINTK("DMA Open M2P - Error\n");
++ return(-1);
++ }
++
++ /*
++ * Get a DMA channel instance for the given hardware device.
++ * If this is a TX look for even numbered channels, else look for
++ * odd numbered channels
++ */
++ if (device >= 10)
++ loop = 1; /* Rx transfer requested */
++ else
++ loop = 0; /* Tx transfer requested */
++
++ for (; loop < 10; loop = loop + 2)
++ if (!dma_chan[loop].ref_count) {
++ /*
++ * Capture the channel and increment the reference count.
++ */
++ channel = loop;
++ dma_chan[channel].ref_count++;
++ break;
++ }
++
++ /*
++ * Unlock the dma channel list.
++ */
++ //spin_unlock_irqrestore(&dma_list_lock, flags);
++ spin_unlock(&dma_list_lock);
++ /*
++ * See if we got a valid channel.
++ */
++ if (channel < 0)
++ return(-1);
++
++ /*
++ * Point regs to the correct dma channel register base.
++ */
++ M2P_reg_base = dma_chan[channel].reg_base;
++
++ /*
++ * Turn on the clock for the specified DMA channel
++ * TODO: need to use the correct register name for the
++ * power control register.
++ */
++ uiPWRCNT = inl(/*SYSCON_PWRCNT*/EP93XX_SYSCON_CLOCK_CONTROL);
++ switch (channel) {
++ case 0:
++ uiPWRCNT |= SYSCON_PWRCNT_DMA_M2PCH0;
++ break;
++
++ case 1:
++ uiPWRCNT |= SYSCON_PWRCNT_DMA_M2PCH1;
++ break;
++
++ case 2:
++ uiPWRCNT |= SYSCON_PWRCNT_DMA_M2PCH2;
++ break;
++
++ case 3:
++ uiPWRCNT |= SYSCON_PWRCNT_DMA_M2PCH3;
++ break;
++
++ case 4:
++ uiPWRCNT |= SYSCON_PWRCNT_DMA_M2PCH4;
++ break;
++
++ case 5:
++ uiPWRCNT |= SYSCON_PWRCNT_DMA_M2PCH5;
++ break;
++
++ case 6:
++ uiPWRCNT |= SYSCON_PWRCNT_DMA_M2PCH6;
++ break;
++
++ case 7:
++ uiPWRCNT |= SYSCON_PWRCNT_DMA_M2PCH7;
++ break;
++
++ case 8:
++ uiPWRCNT |= SYSCON_PWRCNT_DMA_M2PCH8;
++ break;
++
++ case 9:
++ uiPWRCNT |= SYSCON_PWRCNT_DMA_M2PCH9;
++ break;
++
++ default:
++ return(-1);
++ }
++ outl( uiPWRCNT, /*SYSCON_PWRCNT*/EP93XX_SYSCON_CLOCK_CONTROL );
++
++ /*
++ * Clear out the control register before any further setup.
++ */
++ outl( 0, M2P_reg_base+M2P_OFFSET_CONTROL );
++
++ /*
++ * Setup the peripheral port value in the DMA channel registers.
++ */
++ if (device < 10)
++ outl( (unsigned int)device, M2P_reg_base+M2P_OFFSET_PPALLOC );
++ else
++ outl( (unsigned int)(device - 10), M2P_reg_base+M2P_OFFSET_PPALLOC );
++
++ /*
++ * Let's hold on to the value of the Hw device for comparison later.
++ */
++ dma_chan[channel].device = device;
++
++ /*
++ * Success.
++ */
++ return(channel);
++}
++
++/*****************************************************************************
++ *
++ * dma_open_m2m(int device)
++ *
++ * Description: This function will attempt to open a M2M DMA channel.
++ * If the open is successful, the channel number is returned,
++ * otherwise a negative number is returned.
++ *
++ * Parameters:
++ * device: device for which the dma channel is requested.
++ *
++ ****************************************************************************/
++static int
++dma_open_m2m(int device)
++{
++ int channel = -1;
++ unsigned int loop;
++ unsigned int M2M_reg_base;
++ unsigned int uiPWRCNT, uiCONTROL;
++ /*unsigned long flags;*/
++
++ DPRINTK("DMA Open M2M with hw dev %d\n", device);
++
++ /*
++ * Lock the dma channel list.
++ */
++ //spin_lock_irqsave(&dma_list_lock, flags);
++ spin_lock(&dma_list_lock);
++
++
++ /*
++ * Check if this device is already allocated a channel.
++ * TODO: can one M2M device be allocated multiple channels?
++ */
++ for (loop = 10; loop < 12; loop++)
++ /*
++ * Before checking for a matching device, check that the
++ * channel is in use, otherwise the device field is
++ * invalid.
++ */
++ if (dma_chan[loop].ref_count)
++ if (device == dma_chan[loop].device) {
++ DPRINTK("Error - dma_open_m2m - already allocated channel\n");
++
++ /*
++ * Unlock the dma channel list.
++ */
++ //spin_unlock_irqrestore(&dma_list_lock, flags);
++ spin_unlock(&dma_list_lock);
++ /*
++ * Fail.
++ */
++ return(-1);
++ }
++
++ /*
++ * Get a DMA channel instance for the given hardware device.
++ */
++ for (loop = 10; loop < 12; loop++)
++ if (!dma_chan[loop].ref_count) {
++ /*
++ * Capture the channel and increment the reference count.
++ */
++ channel = loop;
++ dma_chan[channel].ref_count++;
++ break;
++ }
++
++ /*
++ * Unlock the dma channel list.
++ */
++ //spin_unlock(dma_list_lock);
++ spin_unlock(&dma_list_lock);
++ //spin_unlock_irqrestore(&dma_list_lock, flags);
++
++ /*
++ * See if we got a valid channel.
++ */
++ if (channel < 0)
++ return(-1);
++
++ /*
++ * Point regs to the correct dma channel register base.
++ */
++ M2M_reg_base = dma_chan[channel].reg_base;
++
++ /*
++ * Turn on the clock for the specified DMA channel
++ * TODO: need to use the correct register name for the
++ * power control register.
++ */
++ uiPWRCNT = inl(/*SYSCON_PWRCNT*/EP93XX_SYSCON_CLOCK_CONTROL);
++ switch (channel) {
++ case 10:
++ uiPWRCNT |= SYSCON_PWRCNT_DMA_M2MCH0;
++ break;
++
++ case 11:
++ uiPWRCNT |= SYSCON_PWRCNT_DMA_M2MCH1;
++ break;
++
++ default:
++ return(-1);
++ }
++ outl( uiPWRCNT, /*SYSCON_PWRCNT*/EP93XX_SYSCON_CLOCK_CONTROL);
++
++ DPRINTK("DMA Open - power control: 0x%x \n", inl(SYSCON_PWRCNT) );
++
++ /*
++ * Clear out the control register before any further setup.
++ */
++ outl( 0, M2M_reg_base+M2M_OFFSET_CONTROL );
++
++ /*
++ * Setup the transfer mode and the request source selection within
++ * the DMA M2M channel registers.
++ */
++ switch (device) {
++ case DMA_MEMORY:
++ /*
++ * Clear TM field, set RSS field to 0
++ */
++ uiCONTROL = inl(M2M_reg_base+M2M_OFFSET_CONTROL);
++ uiCONTROL &= ~(CONTROL_M2M_TM_MASK | CONTROL_M2M_RSS_MASK);
++ outl( uiCONTROL, M2M_reg_base+M2M_OFFSET_CONTROL );
++ break;
++
++ case DMA_IDE:
++ /*
++ * Set RSS field to 3, Set NO_HDSK, Set PW field to 1
++ */
++ uiCONTROL = inl(M2M_reg_base+M2M_OFFSET_CONTROL);
++ uiCONTROL &= ~(CONTROL_M2M_RSS_MASK|CONTROL_M2M_PW_MASK);
++ uiCONTROL |= (3<<CONTROL_M2M_RSS_SHIFT) |
++ CONTROL_M2M_NO_HDSK |
++ (2<<CONTROL_M2M_PW_SHIFT);
++
++ uiCONTROL &= ~(CONTROL_M2M_ETDP_MASK);
++ uiCONTROL &= ~(CONTROL_M2M_DACKP);
++ uiCONTROL &= ~(CONTROL_M2M_DREQP_MASK);
++
++ outl( uiCONTROL, M2M_reg_base+M2M_OFFSET_CONTROL );
++ inl(M2M_reg_base+M2M_OFFSET_CONTROL);
++ break;
++
++ case DMARx_SSP:
++ /*
++ * Set RSS field to 1, Set NO_HDSK, Set TM field to 2
++ */
++ uiCONTROL = inl(M2M_reg_base+M2M_OFFSET_CONTROL);
++ uiCONTROL &= ~(CONTROL_M2M_RSS_MASK|CONTROL_M2M_TM_MASK);
++ uiCONTROL |= (1<<CONTROL_M2M_RSS_SHIFT) |
++ CONTROL_M2M_NO_HDSK |
++ (2<<CONTROL_M2M_TM_SHIFT);
++ outl( uiCONTROL, M2M_reg_base+M2M_OFFSET_CONTROL );
++ break;
++
++ case DMATx_SSP:
++ /*
++ * Set RSS field to 2, Set NO_HDSK, Set TM field to 1
++ */
++ uiCONTROL = inl(M2M_reg_base+M2M_OFFSET_CONTROL);
++ uiCONTROL &= ~(CONTROL_M2M_RSS_MASK|CONTROL_M2M_TM_MASK);
++ uiCONTROL |= (2<<CONTROL_M2M_RSS_SHIFT) |
++ CONTROL_M2M_NO_HDSK |
++ (1<<CONTROL_M2M_TM_SHIFT);
++ outl( uiCONTROL, M2M_reg_base+M2M_OFFSET_CONTROL );
++ break;
++
++ case DMATx_EXT_DREQ:
++ /*
++ * Set TM field to 2, set RSS field to 0
++ */
++ uiCONTROL = inl(M2M_reg_base+M2M_OFFSET_CONTROL);
++ uiCONTROL &= ~(CONTROL_M2M_RSS_MASK|CONTROL_M2M_TM_MASK);
++ uiCONTROL |= 1<<CONTROL_M2M_TM_SHIFT;
++ outl( uiCONTROL, M2M_reg_base+M2M_OFFSET_CONTROL );
++ break;
++
++ case DMARx_EXT_DREQ:
++ /*
++ * Set TM field to 2, set RSS field to 0
++ */
++ uiCONTROL = inl(M2M_reg_base+M2M_OFFSET_CONTROL);
++ uiCONTROL &= ~(CONTROL_M2M_RSS_MASK|CONTROL_M2M_TM_MASK);
++ uiCONTROL |= 2<<CONTROL_M2M_TM_SHIFT;
++ outl( uiCONTROL, M2M_reg_base+M2M_OFFSET_CONTROL );
++ break;
++
++ default:
++ return -1;
++ }
++
++ /*
++ * Let's hold on to the value of the Hw device for comparison later.
++ */
++ dma_chan[channel].device = device;
++
++ /*
++ * Success.
++ */
++ return(channel);
++}
++
++/*****************************************************************************
++ *
++ * int dma_config_m2m(ep93xx_dma_t * dma, unsigned int flags_m2m,
++ * dma_callback callback, unsigned int user_data)
++ *
++ * Description: Configure the DMA channel and install a callback function.
++ * This function will have to be called for every transfer
++ *
++ * dma: Pointer to the dma instance data for the M2M channel to
++ * configure.
++ * flags_m2m Flags used to configure an M2M dma channel and determine
++ * if a callback function and user_data information are included
++ * in this call.
++ * callback function pointer which is called near the end of the
++ * dma channel's irq handler.
++ * user_data defined by the calling driver.
++ *
++ ****************************************************************************/
++static int
++dma_config_m2m(ep93xx_dma_t * dma, unsigned int flags_m2m,
++ dma_callback callback, unsigned int user_data)
++{
++ unsigned long flags;
++ unsigned int M2M_reg_base, uiCONTROL;
++
++ /*
++ * Make sure the channel is disabled before configuring the channel.
++ *
++ * TODO: Is this correct?? Making a big change here...
++ */
++ /* if (!dma->pause || (!dma->pause && dma->xfer_enable)) */
++ if (dma->xfer_enable) {
++ /*
++ * DMA channel is not paused, so we can't configure it.
++ */
++ DPRINTK("DMA channel not paused, so can't configure! \n");
++ return(-1);
++ }
++
++ /*
++ * Mask interrupts.
++ */
++ local_irq_save(flags);
++
++ /*
++ * Setup a pointer into the dma channel's register set.
++ */
++ M2M_reg_base = dma->reg_base;
++
++ uiCONTROL = inl(M2M_reg_base + M2M_OFFSET_CONTROL);
++ outl(0, M2M_reg_base + M2M_OFFSET_CONTROL);
++ inl(M2M_reg_base + M2M_OFFSET_CONTROL);
++ outl(uiCONTROL, M2M_reg_base + M2M_OFFSET_CONTROL);
++
++ /*
++ * By default we disable the stall interrupt.
++ */
++ uiCONTROL = inl(M2M_reg_base+M2M_OFFSET_CONTROL);
++ uiCONTROL &= ~CONTROL_M2M_STALLINTEN;
++ outl( uiCONTROL, M2M_reg_base+M2M_OFFSET_CONTROL );
++
++ /*
++ * By default we disable the done interrupt.
++ */
++ uiCONTROL = inl(M2M_reg_base+M2M_OFFSET_CONTROL);
++ uiCONTROL &= ~CONTROL_M2M_DONEINTEN;
++ outl( uiCONTROL, M2M_reg_base+M2M_OFFSET_CONTROL );
++
++ /*
++ * Set up the transfer control fields based on values passed in
++ * the flags_m2m field.
++ */
++ uiCONTROL = inl(M2M_reg_base+M2M_OFFSET_CONTROL);
++
++ if ( flags_m2m & DESTINATION_HOLD )
++ uiCONTROL |= CONTROL_M2M_DAH;
++ else
++ uiCONTROL &= ~CONTROL_M2M_DAH;
++
++ if ( flags_m2m & SOURCE_HOLD )
++ uiCONTROL |= CONTROL_M2M_SAH;
++ else
++ uiCONTROL &= ~CONTROL_M2M_SAH;
++
++ uiCONTROL &= ~CONTROL_M2M_TM_MASK;
++ uiCONTROL |= (((flags_m2m & TRANSFER_MODE_MASK) >> TRANSFER_MODE_SHIFT) <<
++ CONTROL_M2M_TM_SHIFT) & CONTROL_M2M_TM_MASK;
++
++ uiCONTROL &= ~CONTROL_M2M_PWSC_MASK;
++ uiCONTROL |= (((flags_m2m & WAIT_STATES_MASK) >> WAIT_STATES_SHIFT) <<
++ CONTROL_M2M_PWSC_SHIFT) & CONTROL_M2M_PWSC_MASK;
++
++ outl( uiCONTROL, M2M_reg_base+M2M_OFFSET_CONTROL );
++ inl(M2M_reg_base + M2M_OFFSET_CONTROL);
++
++ /*
++ * Save the callback function in the dma instance for this channel.
++ */
++ dma->callback = callback;
++
++ /*
++ * Save the user data in the the dma instance for this channel.
++ */
++ dma->user_data = user_data;
++
++ /*
++ * Put the dma instance into the pause state by setting the
++ * pause bit to true.
++ */
++ dma->pause = TRUE;
++
++ local_irq_restore(flags);
++
++ /*
++ * Success.
++ */
++ return(0);
++}
++
++/*****************************************************************************
++ *
++ * int dma_start(int handle, unsigned int channels, unsigned int * handles)
++ *
++ * Description: Initiate a transfer on up to 3 channels.
++ *
++ * handle: handle for the channel to initiate transfer on.
++ * channels: number of channels to initiate transfers on.
++ * handles: pointer to an array of handles, one for each channel which
++ * is to be started.
++ *
++ ****************************************************************************/
++static int
++dma_start_m2m(int channel, ep93xx_dma_t * dma)
++{
++ unsigned long flags;
++ unsigned int M2M_reg_base = dma->reg_base;
++ unsigned int uiCONTROL;
++
++ /*
++ * Mask interrupts while we get this started.
++ */
++ local_irq_save(flags);
++
++ /*
++ * Make sure the channel has at least one buffer in the queue.
++ */
++ if (dma->new_buffers < 1) {
++ /*
++ * Unmask irqs
++ */
++ local_irq_restore(flags);
++
++ DPRINTK("DMA Start: Channel starved.\n");
++
++ /*
++ * This channel does not have enough buffers queued up,
++ * so enter the pause by starvation state.
++ */
++ dma->xfer_enable = TRUE;
++ dma->pause = TRUE;
++
++ /*
++ * Success.
++ */
++ return(0);
++ }
++
++ /*
++ * Clear any pending interrupts.
++ */
++ outl(0x0, M2M_reg_base+M2M_OFFSET_INTERRUPT);
++
++ /*
++ * Set up one or both buffer descriptors with values from the next one or
++ * two buffers in the queue. By default disable the next frame buffer
++ * interrupt on the channel.
++ */
++ uiCONTROL = inl(M2M_reg_base+M2M_OFFSET_CONTROL);
++ uiCONTROL &= ~CONTROL_M2M_NFBINTEN;
++ outl( uiCONTROL, M2M_reg_base+M2M_OFFSET_CONTROL );
++
++ /*
++ * enable the done interrupt.
++ */
++ uiCONTROL = inl(M2M_reg_base+M2M_OFFSET_CONTROL);
++ uiCONTROL |= CONTROL_M2M_DONEINTEN;
++ outl( uiCONTROL, M2M_reg_base+M2M_OFFSET_CONTROL );
++
++ /*
++ * Update the dma channel instance transfer state.
++ */
++ dma->xfer_enable = TRUE;
++ dma->pause = FALSE;
++
++ /*
++ * Program up the first buffer descriptor with a source and destination
++ * and a byte count.
++ */
++ outl( dma->buffer_queue[dma->current_buffer].source,
++ M2M_reg_base+M2M_OFFSET_SAR_BASE0 );
++
++ outl( dma->buffer_queue[dma->current_buffer].dest,
++ M2M_reg_base+M2M_OFFSET_DAR_BASE0 );
++
++ outl( dma->buffer_queue[dma->current_buffer].size,
++ M2M_reg_base+M2M_OFFSET_BCR0 );
++
++ /*
++ * Decrement the new buffers counter.
++ */
++ dma->new_buffers--;
++
++ /*
++ * Set up the second buffer descriptor with a second buffer if we have
++ * a second buffer.
++ */
++ if (dma->new_buffers) {
++ outl( dma->buffer_queue[(dma->current_buffer + 1) %
++ MAX_EP93XX_DMA_BUFFERS].source,
++ M2M_reg_base+M2M_OFFSET_SAR_BASE1 );
++
++ outl( dma->buffer_queue[(dma->current_buffer + 1) %
++ MAX_EP93XX_DMA_BUFFERS].dest,
++ M2M_reg_base+M2M_OFFSET_DAR_BASE1 );
++
++ outl( dma->buffer_queue[(dma->current_buffer + 1) %
++ MAX_EP93XX_DMA_BUFFERS].size,
++ M2M_reg_base+M2M_OFFSET_BCR1 );
++
++ uiCONTROL = inl(M2M_reg_base+M2M_OFFSET_CONTROL);
++ uiCONTROL |= CONTROL_M2M_NFBINTEN;
++ outl( uiCONTROL, M2M_reg_base+M2M_OFFSET_CONTROL );
++
++ dma->new_buffers--;
++ }
++
++ /*
++ * Now we enable the channel. This initiates the transfer.
++ */
++ uiCONTROL = inl(M2M_reg_base+M2M_OFFSET_CONTROL);
++ uiCONTROL |= CONTROL_M2M_ENABLE;
++ outl( uiCONTROL, M2M_reg_base+M2M_OFFSET_CONTROL );
++ inl(M2M_reg_base + M2M_OFFSET_CONTROL);
++
++ /*
++ * If this is a memory to memory transfer, we need to s/w trigger the
++ * transfer by setting the start bit within the control register.
++ */
++ if (dma->device == DMA_MEMORY) {
++ uiCONTROL = inl(M2M_reg_base+M2M_OFFSET_CONTROL);
++ uiCONTROL |= CONTROL_M2M_START;
++ outl( uiCONTROL, M2M_reg_base+M2M_OFFSET_CONTROL );
++ }
++
++ DPRINTK("DMA - It's been started!!");
++ DPRINTK("CONTROL - 0x%x \n", inl(M2M_reg_base+M2M_OFFSET_CONTROL) );
++ DPRINTK("STATUS - 0x%x \n", inl(M2M_reg_base+M2M_OFFSET_STATUS) );
++ DPRINTK("BCR0 - 0x%x \n", dma->buffer_queue[dma->current_buffer].size);
++ DPRINTK("SAR_BASE0 - 0x%x \n", inl(M2M_reg_base+M2M_OFFSET_SAR_BASE0) );
++ DPRINTK("SAR_CUR0 - 0x%x \n", inl(M2M_reg_base+M2M_OFFSET_SAR_CURRENT0) );
++ DPRINTK("DAR_BASE0 - 0x%x \n", inl(M2M_reg_base+M2M_OFFSET_DAR_BASE0) );
++ DPRINTK("DAR_CUR0 - 0x%x \n", inl(M2M_reg_base+M2M_OFFSET_DAR_CURRENT0) );
++
++ /*
++ * Unmask irqs
++ */
++ local_irq_restore(flags);
++
++ /*
++ * Success.
++ */
++ return(0);
++}
++
++/*****************************************************************************
++ *
++ * DMA interface functions
++ *
++ ****************************************************************************/
++
++/*****************************************************************************
++ *
++ * int dma_init(int handle, unsigned int flags_m2p, unsigned int flags_m2m,
++ * dma_callback callback, unsigned int user_data)
++ *
++ * Description: Configure the DMA channel and install a callback function.
++ *
++ * handle: Handle unique the each instance of the dma interface, used
++ * to verify this call.
++ * flags_m2p Flags used to configure an M2P/P2M dma channel and determine
++ * if a callback function and user_data information are included
++ * in this call. This field should be NULL if handle represents
++ * an M2M channel.
++ * flags_m2m Flags used to configure an M2M dma channel and determine
++ * if a callback function and user_data information are included
++ * in this call. This field should be NULL if handle represents
++ * an M2P/P2M channel.
++ * callback function pointer which is called near the end of the
++ * dma channel's irq handler.
++ * user_data defined by the calling driver.
++ *
++ ****************************************************************************/
++int
++ep93xx_dma_config(int handle, unsigned int flags_m2p, unsigned int flags_m2m,
++ dma_callback callback, unsigned int user_data)
++{
++ int channel;
++ ep93xx_dma_t * dma;
++ unsigned long flags;
++ unsigned int M2P_reg_base, uiCONTROL;
++
++ /*
++ * Get the DMA hw channel # from the handle.
++ */
++ channel = dma_get_channel_from_handle(handle);
++
++ /*
++ * See if this is a valid handle.
++ */
++ if (channel < 0) {
++ printk(KERN_ERR
++ "DMA Config: Invalid dma handle.\n");
++ return(-EINVAL);
++ }
++
++ DPRINTK("DMA Config \n");
++
++ dma = &dma_chan[channel];
++
++ local_irq_save(flags);
++
++ /*
++ * Check if the channel is currently transferring.
++ */
++ if (dma->xfer_enable) {
++ local_irq_restore(flags);
++ return(-EINVAL);
++ }
++
++ /*
++ * Check if this is an m2m function.
++ */
++ if (channel >= 10) {
++ local_irq_restore(flags);
++
++ /*
++ * Call another function to handle m2m config.
++ */
++ return(dma_config_m2m(dma, flags_m2m, callback, user_data));
++ }
++
++ /*
++ * Setup a pointer into the dma channel's register set.
++ */
++ M2P_reg_base = dma->reg_base;
++
++ /*
++ * By default we enable the stall interrupt.
++ */
++ uiCONTROL = inl(M2P_reg_base+M2P_OFFSET_CONTROL);
++ uiCONTROL |= CONTROL_M2P_STALLINTEN;
++ outl( uiCONTROL, M2P_reg_base+M2P_OFFSET_CONTROL );
++
++ /*
++ * Configure the channel for an error from the peripheral.
++ */
++ uiCONTROL = inl(M2P_reg_base+M2P_OFFSET_CONTROL);
++ if ( flags_m2p && CHANNEL_ERROR_INT_ENABLE )
++ uiCONTROL |= CONTROL_M2P_CHERRORINTEN;
++ else
++ uiCONTROL &= ~CONTROL_M2P_CHERRORINTEN;
++ outl( uiCONTROL, M2P_reg_base+M2P_OFFSET_CONTROL );
++
++ uiCONTROL = inl(M2P_reg_base+M2P_OFFSET_CONTROL);
++ if ( flags_m2p && CHANNEL_ABORT )
++ uiCONTROL |= CONTROL_M2P_ABRT;
++ else
++ uiCONTROL &= ~CONTROL_M2P_ABRT;
++ outl( uiCONTROL, M2P_reg_base+M2P_OFFSET_CONTROL );
++
++ uiCONTROL = inl(M2P_reg_base+M2P_OFFSET_CONTROL);
++ if ( flags_m2p && IGNORE_CHANNEL_ERROR )
++ uiCONTROL |= CONTROL_M2P_ICE;
++ else
++ uiCONTROL &= ~CONTROL_M2P_ICE;
++ outl( uiCONTROL, M2P_reg_base+M2P_OFFSET_CONTROL );
++
++ /*
++ * Save the callback function in the dma instance for this channel.
++ */
++ dma->callback = callback;
++
++ /*
++ * Save the user data in the the dma instance for this channel.
++ */
++ dma->user_data = user_data;
++
++ /*
++ * Put the dma instance into the pause state by setting the
++ * pause bit to true.
++ */
++ dma->pause = TRUE;
++
++ local_irq_restore(flags);
++
++ /*
++ * Success.
++ */
++ return(0);
++}
++
++/*****************************************************************************
++ *
++ * int dma_start(int handle, unsigned int channels, unsigned int * handles)
++ *
++ * Description: Initiate a transfer on up to 3 channels.
++ *
++ * handle: handle for the channel to initiate transfer on.
++ * channels: number of channels to initiate transfers on.
++ * handles: pointer to an array of handles, one for each channel which
++ * is to be started.
++ *
++ ****************************************************************************/
++int
++ep93xx_dma_start(int handle, unsigned int channels, unsigned int * handles)
++{
++ ep93xx_dma_t * dma_pointers[3];
++ unsigned int M2P_reg_bases[3];
++ unsigned int loop, uiCONTROL;
++ unsigned long flags;
++ int channel;
++
++ /*
++ * Get the DMA hw channel # from the handle.
++ */
++ channel = dma_get_channel_from_handle(handle);
++
++ /*
++ * See if this is a valid handle.
++ */
++ if (channel < 0) {
++ printk(KERN_ERR "DMA Start: Invalid dma handle.\n");
++ return(-EINVAL);
++ }
++
++ if (channels < 1) {
++ printk(KERN_ERR "DMA Start: Invalid parameter.\n");
++ return(-EINVAL);
++ }
++
++ DPRINTK("DMA Start \n");
++
++ /*
++ * Mask off registers.
++ */
++ local_irq_save(flags);
++
++ /*
++ * Check if this is a start multiple.
++ */
++ if (channels > 1) {
++ DPRINTK("DMA ERROR: Start, multiple start not supported yet \n");
++ return(-1);
++ } else {
++ /*
++ * Check if this channel is already transferring.
++ */
++ if (dma_chan[channel].xfer_enable && !dma_chan[channel].pause) {
++ printk(KERN_ERR
++ "DMA Start: Invalid command for channel %d.\n", channel);
++
++ /*
++ * Unmask irqs
++ */
++ local_irq_restore(flags);
++
++ /*
++ * This channel is already transferring, so return an error.
++ */
++ return(-EINVAL);
++ }
++
++ /*
++ * If this is an M2M channel, call a different function.
++ */
++ if (channel >= 10) {
++ /*
++ * Unmask irqs
++ */
++ local_irq_restore(flags);
++
++ /*
++ * Call the m2m start function. Only start one channel.
++ */
++ return(dma_start_m2m(channel, &dma_chan[channel]));
++ }
++
++ /*
++ * Make sure the channel has at least one buffer in the queue.
++ */
++ if (dma_chan[channel].new_buffers < 1) {
++ DPRINTK("DMA Start: Channel starved.\n");
++
++ /*
++ * This channel does not have enough buffers queued up,
++ * so enter the pause by starvation state.
++ */
++ dma_chan[channel].xfer_enable = TRUE;
++ dma_chan[channel].pause = TRUE;
++
++ /*
++ * Unmask irqs
++ */
++ local_irq_restore(flags);
++
++ /*
++ * Success.
++ */
++ return(0);
++ }
++
++ /*
++ * Set up a dma instance pointer for this dma channel.
++ */
++ dma_pointers[0] = &dma_chan[channel];
++
++ /*
++ * Set up a pointer to the register set for this channel.
++ */
++ M2P_reg_bases[0] = dma_pointers[0]->reg_base;
++ }
++
++ /*
++ * Setup both MAXCNT registers with values from the next two buffers
++ * in the queue, and enable the next frame buffer interrupt on the channel.
++ */
++ for (loop = 0; loop < channels; loop++) {
++ /*
++ * Check if we need to restore a paused transfer.
++ */
++ if (dma_pointers[loop]->pause_buf.buf_id != -1)
++ outl( dma_pointers[loop]->pause_buf.size,
++ M2P_reg_bases[loop]+M2P_OFFSET_MAXCNT0 );
++ else
++ outl( dma_pointers[loop]->buffer_queue[dma_pointers[loop]->current_buffer].size,
++ M2P_reg_bases[loop]+M2P_OFFSET_MAXCNT0 );
++ }
++
++ for (loop = 0; loop < channels; loop++) {
++ /*
++ * Enable the specified dma channels.
++ */
++ uiCONTROL = inl(M2P_reg_bases[loop]+M2P_OFFSET_CONTROL);
++ uiCONTROL |= CONTROL_M2P_ENABLE;
++ outl( uiCONTROL, M2P_reg_bases[loop]+M2P_OFFSET_CONTROL );
++
++ /*
++ * Update the dma channel instance transfer state.
++ */
++ dma_pointers[loop]->xfer_enable = TRUE;
++ dma_pointers[loop]->pause = FALSE;
++ }
++
++ /*
++ * Program up the BASE0 registers for all specified channels, this
++ * will initiate transfers on all specified channels.
++ */
++ for (loop = 0; loop < channels; loop++)
++ /*
++ * Check if we need to restore a paused transfer.
++ */
++ if (dma_pointers[loop]->pause_buf.buf_id != -1) {
++ outl( dma_pointers[loop]->pause_buf.source,
++ M2P_reg_bases[loop]+M2P_OFFSET_BASE0 );
++
++ /*
++ * Set the pause buffer to NULL
++ */
++ dma_pointers[loop]->pause_buf.buf_id = -1;
++ dma_pointers[loop]->pause_buf.size = 0;
++ } else if(dma_pointers[loop]->new_buffers){
++ outl( dma_pointers[loop]->buffer_queue[
++ dma_pointers[loop]->current_buffer].source,
++ M2P_reg_bases[loop]+M2P_OFFSET_BASE0 );
++ dma_pointers[loop]->new_buffers--;
++
++ }
++
++ /*
++ * Before restoring irqs setup the second MAXCNT/BASE
++ * register with a second buffer.
++ */
++ for (loop = 0; loop < channels; loop++)
++ if (dma_pointers[loop]->new_buffers) {
++ /*
++ * By default we enable the next frame buffer interrupt.
++ */
++ uiCONTROL = inl(M2P_reg_bases[loop]+M2P_OFFSET_CONTROL);
++ uiCONTROL |= CONTROL_M2P_NFBINTEN;
++ outl( uiCONTROL, M2P_reg_bases[loop]+M2P_OFFSET_CONTROL );
++
++ outl( dma_pointers[loop]->buffer_queue[
++ (dma_pointers[loop]->current_buffer + 1) %
++ MAX_EP93XX_DMA_BUFFERS].size,
++ M2P_reg_bases[loop]+M2P_OFFSET_MAXCNT1 );
++
++ outl( dma_pointers[loop]->buffer_queue[
++ (dma_pointers[loop]->current_buffer + 1) %
++ MAX_EP93XX_DMA_BUFFERS].source,
++ M2P_reg_bases[loop]+M2P_OFFSET_BASE1 );
++ dma_pointers[loop]->new_buffers--;
++ }
++
++ /*
++ DPRINTK("DMA - It's been started!!");
++ DPRINTK("STATUS - 0x%x \n", inl(M2P_reg_base+M2P_OFFSET_STATUS) );
++ DPRINTK("CONTROL - 0x%x \n", inl(M2P_reg_base+M2P_OFFSET_CONTROL) );
++ DPRINTK("REMAIN - 0x%x \n", inl(M2P_reg_base+M2P_OFFSET_REMAIN) );
++ DPRINTK("PPALLOC - 0x%x \n", inl(M2P_reg_base+M2P_OFFSET_PPALLOC) );
++ DPRINTK("BASE0 - 0x%x \n", inl(M2P_reg_base+M2P_OFFSET_BASE0) );
++ DPRINTK("MAXCNT0 - 0x%x \n", inl(M2P_reg_base+M2P_OFFSET_MAXCNT0) );
++ DPRINTK("CURRENT0 - 0x%x \n", inl(M2P_reg_base+M2P_OFFSET_CURRENT0) );
++ DPRINTK("BASE1 - 0x%x \n", inl(M2P_reg_base+M2P_OFFSET_BASE1) );
++ DPRINTK("MAXCNT1 - 0x%x \n", inl(M2P_reg_base+M2P_OFFSET_MAXCNT1) );
++ DPRINTK("CURRENT1 - 0x%x \n", inl(M2P_reg_base+M2P_OFFSET_CURRENT1) );
++
++ DPRINTK("Pause - %d \n", dma_pointers[0]->pause);
++ DPRINTK("xfer_enable - %d \n", dma_pointers[0]->xfer_enable);
++ DPRINTK("total bytes - 0x%x \n", dma_pointers[0]->total_bytes);
++ DPRINTK("total buffer - %d \n", dma_pointers[0]->total_buffers);
++ DPRINTK("new buffers - %d \n", dma_pointers[0]->new_buffers);
++ DPRINTK("current buffer - %d \n", dma_pointers[0]->current_buffer);
++ DPRINTK("last buffer - %d \n", dma_pointers[0]->last_buffer);
++ DPRINTK("used buffers - %d \n", dma_pointers[0]->used_buffers);
++ */
++ /*
++ * Unmask irqs
++ */
++ local_irq_restore(flags);
++
++ /*
++ * Success.
++ */
++ return(0);
++}
++
++/*****************************************************************************
++ *
++ * int ep93xx_dma_add_buffer(int handle, unsigned int * address,
++ * unsigned int size, unsigned int last)
++ *
++ * Description: Add a buffer entry to the DMA buffer queue.
++ *
++ * handle: handle for the channel to add this buffer to.
++ * address: Pointer to an integer which is the start address of the
++ * buffer which is to be added to the queue.
++ * size: size of the buffer in bytes.
++ * last: 1 if this is the last buffer in this stream, 0 otherwise.
++ *
++ ****************************************************************************/
++int
++ep93xx_dma_add_buffer(int handle, unsigned int source, unsigned int dest,
++ unsigned int size, unsigned int last,
++ unsigned int buf_id)
++{
++ unsigned long flags;
++ ep93xx_dma_t * dma;
++ int channel;
++#if 0
++ static int peak_total_buffers=0;
++#endif
++ /*
++ * Get the DMA hw channel # from the handle.
++ */
++ channel = dma_get_channel_from_handle(handle);
++
++ /*
++ * See if this is a valid handle.
++ */
++ if (channel < 0) {
++ printk(KERN_ERR
++ "DMA Add Buffer: Invalid dma handle.\n");
++ return(-EINVAL);
++ }
++
++ /*
++ * Get a pointer to the dma instance.
++ */
++ dma = &dma_chan[channel];
++
++#if 0
++ if( dma->total_buffers > peak_total_buffers )
++ {
++ peak_total_buffers=dma->total_buffers;
++ printk("peak_total_buffers=%d\n", peak_total_buffers );
++ }
++#endif
++ /*
++ * Mask interrupts and hold on to the original state.
++ */
++ local_irq_save(flags);
++
++ /*
++ * If the buffer queue is full, last_buffer is the same as current_buffer and
++ * we're not tranfering, or last_buffer is pointing to a used buffer, then exit.
++ * TODO: do I need to do any more checks?
++ */
++ if (dma->total_buffers >= MAX_EP93XX_DMA_BUFFERS)
++ {
++ DPRINTK("too many dma buffers: MAX_EP93XX_DMA_BUFFERS set to low ?\n");
++ /*
++ * Restore the state of the irqs
++ */
++ local_irq_restore(flags);
++
++ /*
++ * Fail.
++ */
++ return(-1);
++ }
++
++ /*
++ * Add this buffer to the queue
++ */
++ dma->buffer_queue[dma->last_buffer].source = source;
++ dma->buffer_queue[dma->last_buffer].dest = dest;
++ dma->buffer_queue[dma->last_buffer].size = size;
++ dma->buffer_queue[dma->last_buffer].last = last;
++ dma->buffer_queue[dma->last_buffer].buf_id = buf_id;
++
++ /*
++ * Reset the used field of the buffer structure.
++ */
++ dma->buffer_queue[dma->last_buffer].used = FALSE;
++
++ /*
++ * Increment the End Item Pointer.
++ */
++ dma->last_buffer = (dma->last_buffer + 1) % MAX_EP93XX_DMA_BUFFERS;
++
++ /*
++ * Increment the new buffers counter and the total buffers counter
++ */
++ dma->new_buffers++;
++ dma->total_buffers++;
++
++ /*
++ * restore the interrupt state.
++ */
++ local_irq_restore(flags);
++
++ /*
++ * Check if the channel was starved into a stopped state.
++ */
++ if (dma->pause && dma->xfer_enable) {
++ if (dma->new_buffers >= 1) {
++ DPRINTK("DMA - calling start from add after starve. \n");
++
++ /*
++ * The channel was starved into a stopped state, and we've got
++ * 2 new buffers, so start tranferring again.
++ */
++ ep93xx_dma_start(handle, 1, 0);
++ }
++ }
++
++ /*
++ * Success.
++ */
++ return(0);
++}
++
++/*****************************************************************************
++ *
++ * int ep93xx_dma_remove_buffer(int handle, unsigned int * address,
++ * unsigned int * size)
++ *
++ * Description: Remove a buffer entry from the DMA buffer queue. If
++ * buffer was removed successfully, return 0, otherwise
++ * return -1.
++ *
++ * handle: handle for the channel to remove a buffer from.
++ * address: Pointer to an integer which is filled in with the start
++ * address of the removed buffer.
++ * size: Pointer to an integer which is filled in with the size in
++ * bytes of the removed buffer.
++ *
++ ****************************************************************************/
++int
++ep93xx_dma_remove_buffer(int handle, unsigned int * buf_id)
++{
++ unsigned int test;
++ unsigned int loop;
++ int return_val = -1;
++ unsigned long flags;
++ ep93xx_dma_t *dma;
++ int channel;
++
++ /*
++ * Get the DMA hw channel # from the handle.
++ */
++ channel = dma_get_channel_from_handle(handle);
++
++ /*
++ * See if this is a valid handle.
++ */
++ if (channel < 0) {
++ printk(KERN_ERR
++ "DMA Remove Buffer: Invalid dma handle.\n");
++ return(-EINVAL);
++ }
++
++ dma = &dma_chan[channel];
++
++ /*
++ * Mask interrupts and hold on to the original state.
++ */
++ local_irq_save(flags);
++
++ /*
++ * Make sure there are used buffers to be returned.
++ */
++ if (dma->used_buffers) {
++ test = dma->last_buffer;
++
++ for (loop = 0; loop < MAX_EP93XX_DMA_BUFFERS; loop++) {
++ if (dma->buffer_queue[test].used && (dma->buffer_queue[test].buf_id != -1)) {
++ /*DPRINTK("buffer %d used \n", test); */
++
++ /*
++ * This is a used buffer, fill in the buf_id pointer
++ * with the buf_id for this buffer.
++ */
++ *buf_id = dma->buffer_queue[test].buf_id;
++
++ /*
++ * Reset this buffer structure
++ */
++ dma->buffer_queue[test].buf_id = -1;
++
++ /*
++ * Decrement the used buffer counter, and the total buffer counter.
++ */
++ dma->used_buffers--;
++ dma->total_buffers--;
++
++ /*
++ * Successful removal of a buffer, so set the return
++ * value to 0, then exit this loop.
++ */
++ return_val = 0;
++ break;
++ }
++
++ /*
++ * This buffer isn't used, let's see if the next one is.
++ */
++ test = (test + 1) % MAX_EP93XX_DMA_BUFFERS;
++ }
++ }
++
++ /*
++ * Restore interrupts.
++ */
++ local_irq_restore(flags);
++
++ /*
++ * Success.
++ */
++ return(return_val);
++}
++
++/*****************************************************************************
++ *
++ * int ep93xx_dma_pause(int handle, unsigned int channels,
++ * unsigned int * handles)
++ *
++ * Description: Disable any ongoing transfer for the given channel, retaining
++ * the state of the current buffer transaction so that upon
++ * resume, the dma will continue where it left off.
++ *
++ * handle: Handle for the channel to be paused. If this is a pause for
++ * for multiple channels, handle is a valid handle for one of
++ * the channels to be paused.
++ * channels: number of channel to pause transfers on.
++ * handles: Pointer to an array of handles, one for each channel which
++ * to be paused. If this pause is intended only for one
++ * channel, this field should be set to NULL.
++ *
++ ****************************************************************************/
++int
++ep93xx_dma_pause(int handle, unsigned int channels, unsigned int * handles)
++{
++ unsigned long flags;
++ ep93xx_dma_t * dma;
++ int channel;
++
++ DPRINTK("ep93xx_dma_pause \n");
++
++ /*
++ * Mask interrupts and hold on to the original state.
++ */
++ local_irq_save(flags);
++
++ /*
++ * Get the DMA hw channel # from the handle.
++ */
++ channel = dma_get_channel_from_handle(handle);
++
++ /*
++ * See if this is a valid handle.
++ */
++ if (channel < 0) {
++ /*
++ * restore interrupts.
++ */
++ local_irq_restore(flags);
++
++ printk(KERN_ERR
++ "DMA Pause: Invalid dma handle.\n");
++
++ /*
++ * Fail.
++ */
++ return(-EINVAL);
++ }
++
++ DPRINTK("DMA %d: pause \n", channel);
++
++ /*
++ * Set up a pointer to the dma instance data.
++ */
++ dma = &dma_chan[channel];
++
++ /*
++ * Check if we're already paused.
++ */
++ if (dma->pause) {
++ /*
++ * We're paused, but are we stopped?
++ */
++ if (dma->xfer_enable)
++ /*
++ * Put the channel in the stopped state.
++ */
++ dma->xfer_enable = FALSE;
++
++ DPRINTK("DMA Pause - already paused.");
++ } else {
++ /*
++ * Put the channel into the stopped state.
++ */
++ dma->xfer_enable = FALSE;
++ dma->pause = TRUE;
++ }
++
++ /*
++ * restore interrupts.
++ */
++ local_irq_restore(flags);
++
++ /*
++ * Already paused, so exit.
++ */
++ return(0);
++}
++
++/*****************************************************************************
++ *
++ * void ep93xx_dma_flush(int handle)
++ *
++ * Description: Flushes all queued buffers and transfers in progress
++ * for the given channel. Return the buffer entries
++ * to the calling function.
++ *
++ * handle: handle for the channel for which the flush is intended.
++ *
++ ****************************************************************************/
++int
++ep93xx_dma_flush(int handle)
++{
++ unsigned int loop;
++ unsigned long flags;
++ ep93xx_dma_t * dma;
++ int channel;
++ unsigned int M2P_reg_base,uiCONTROL;
++
++ /*
++ * Get the DMA hw channel # from the handle.
++ */
++ channel = dma_get_channel_from_handle(handle);
++
++ /*
++ * See if this is a valid handle.
++ */
++ if (channel < 0) {
++ printk(KERN_ERR "DMA Flush: Invalid dma handle.\n");
++ return(-EINVAL);
++ }
++
++ DPRINTK("DMA %d: flush \n", channel);
++
++ /*
++ * Set up a pointer to the dma instance data for this channel
++ */
++ dma = &dma_chan[channel];
++
++ /*
++ * Mask interrupts and hold on to the original state.
++ */
++ local_irq_save(flags);
++
++ /*
++ * Disable the dma channel
++ */
++ if (channel < 10) {
++ /*
++ * M2P channel
++ */
++ uiCONTROL = inl(dma->reg_base+M2P_OFFSET_CONTROL);
++ uiCONTROL &= ~CONTROL_M2P_ENABLE;
++ outl( uiCONTROL, dma->reg_base+M2P_OFFSET_CONTROL );
++ } else {
++ /*
++ * M2M channel
++ */
++ uiCONTROL = inl(dma->reg_base+M2M_OFFSET_CONTROL);
++ uiCONTROL &= ~CONTROL_M2M_ENABLE;
++ outl( uiCONTROL, dma->reg_base+M2M_OFFSET_CONTROL );
++ }
++
++ for (loop = 0; loop < MAX_EP93XX_DMA_BUFFERS; loop++)
++ {
++ dma->buffer_queue[loop].buf_id = -1;
++ dma->buffer_queue[loop].last = 0;
++ }
++
++ /*
++ * Set the Current and Last item to zero.
++ */
++ dma->current_buffer = 0;
++ dma->last_buffer = 0;
++
++ /*
++ * Reset the Buffer counters
++ */
++ dma->used_buffers = 0;
++ dma->new_buffers = 0;
++ dma->total_buffers = 0;
++
++ /*
++ * reset the Total bytes counter.
++ */
++ dma->total_bytes = 0;
++
++ /*
++ * Reset the paused buffer.
++ */
++ dma->pause_buf.last = 0;
++ dma->pause_buf.buf_id = -1;
++
++ M2P_reg_base = dma_chan[channel].reg_base;
++
++ /*
++ * restore interrupts.
++ */
++ local_irq_restore(flags);
++
++ /*
++ * Success.
++ */
++ return(0);
++}
++
++/*****************************************************************************
++ *
++ * int ep93xx_dma_queue_full(int handle)
++ *
++ * Description: Query to determine if the DMA queue of buffers for
++ * a given channel is full.
++ * 0 = queue is full
++ * 1 = queue is not full
++ *
++ * handle: handle for the channel to query.
++ *
++ ****************************************************************************/
++int
++ep93xx_dma_queue_full(int handle)
++{
++ int list_full = 0;
++ unsigned long flags;
++ int channel;
++
++ /*
++ * Get the DMA hw channel # from the handle.
++ */
++ channel = dma_get_channel_from_handle(handle);
++
++ /*
++ * See if this is a valid handle.
++ */
++ if (channel < 0) {
++ printk(KERN_ERR "DMA Queue Full: Invalid dma handle.\n");
++ return(-EINVAL);
++ }
++
++ DPRINTK("DMA %d: queue full \n", channel);
++
++ /*
++ * Mask interrupts and hold on to the original state.
++ */
++ local_irq_save(flags);
++
++ /*
++ * If the last item is equal to the used item then
++ * the queue is full.
++ */
++ if (dma_chan[channel].total_buffers < MAX_EP93XX_DMA_BUFFERS)
++ list_full = FALSE;
++ else
++ list_full = TRUE;
++
++ /*
++ * restore interrupts.
++ */
++ local_irq_restore(flags);
++
++ return(list_full);
++}
++
++/*****************************************************************************
++ *
++ * int ep93xx_dma_get_position()
++ *
++ * Description: Takes two integer pointers and fills them with the start
++ * and current address of the buffer currently transferring
++ * on the specified DMA channel.
++ *
++ * handle handle for the channel to query.
++ * *buf_id buffer id for the current buffer transferring on the
++ * dma channel.
++ * *total total bytes transferred on the channel. Only counts
++ * whole buffers transferred.
++ * *current_frac number of bytes transferred so far in the current buffer.
++ ****************************************************************************/
++int
++ep93xx_dma_get_position(int handle, unsigned int * buf_id,
++ unsigned int * total, unsigned int * current_frac )
++{
++ int channel;
++ ep93xx_dma_t * dma;
++ unsigned int buf_id1, total1, current_frac1, buf_id2, total2;
++ unsigned int Status, NextBuffer, StateIsBufNext, M2P_reg_base=0;
++ unsigned int pause1, pause2;
++
++ /*
++ * Get the DMA hw channel # from the handle. See if this is a
++ * valid handle.
++ */
++ channel = dma_get_channel_from_handle(handle);
++ if (channel < 0) {
++ printk(KERN_ERR "DMA Get Position: Invalid dma handle.\n");
++ return(-EINVAL);
++ }
++
++ dma = &dma_chan[channel];
++
++ /*
++ * If DMA moves to a new buffer in the middle of us grabbing the
++ * buffer info, then do it over again.
++ */
++ do{
++ buf_id1 = dma->buffer_queue[dma->current_buffer].buf_id;
++ total1 = dma->total_bytes;
++ pause1 = dma->pause;
++
++ if (channel < 10) {
++ // M2P
++ M2P_reg_base = dma->reg_base;
++
++ Status = inl(M2P_reg_base+M2P_OFFSET_STATUS);
++
++ NextBuffer = ((Status & STATUS_M2P_NEXTBUFFER) != 0);
++
++ StateIsBufNext = ((Status & STATUS_M2P_CURRENT_MASK) ==
++ STATUS_M2P_DMA_BUF_NEXT);
++
++ if( NextBuffer ^ StateIsBufNext )
++ current_frac1 = inl(M2P_reg_base+M2P_OFFSET_CURRENT1) -
++ inl(M2P_reg_base+M2P_OFFSET_BASE1);
++ else
++ current_frac1 = inl(M2P_reg_base+M2P_OFFSET_CURRENT0) -
++ inl(M2P_reg_base+M2P_OFFSET_BASE0);
++
++ } else {
++ // M2M - TODO implement this for M2M
++ current_frac1 = 0;
++ }
++
++ buf_id2 = dma->buffer_queue[dma->current_buffer].buf_id;
++ total2 = dma->total_bytes;
++ pause2 = dma->pause;
++
++ } while ( (buf_id1 != buf_id2) || (total1 != total2) || (pause1 != pause2) );
++
++ if (pause1)
++ current_frac1 = 0;
++
++ if (buf_id)
++ *buf_id = buf_id1;
++
++ if (total)
++ *total = total1;
++
++ if (current_frac)
++ *current_frac = current_frac1;
++
++// DPRINTK("DMA buf_id %d, total %d, frac %d\n", buf_id1, total1, current_frac1);
++
++ /*
++ * Success.
++ */
++ return(0);
++}
++
++/*****************************************************************************
++ *
++ * int ep93xx_dma_get_total(int handle)
++ *
++ * Description: Returns the total number of bytes transferred on the
++ * specified channel since the channel was requested.
++ *
++ * handle: handle for the channel to query.
++ *
++ ****************************************************************************/
++int
++ep93xx_dma_get_total(int handle)
++{
++ int channel;
++
++ /*
++ * Get the DMA hw channel # from the handle.
++ */
++ channel = dma_get_channel_from_handle(handle);
++
++ /*
++ * See if this is a valid handle.
++ */
++ if (channel < 0) {
++ printk(KERN_ERR "DMA Get Total: Invalid dma handle.\n");
++ return(-EINVAL);
++ }
++
++ DPRINTK("DMA %d: total: %d \n", channel, dma_chan[channel].total_bytes);
++
++ /*
++ * Return the total number of bytes transferred on this channel since
++ * it was requested.
++ */
++ return(dma_chan[channel].total_bytes);
++}
++
++/*****************************************************************************
++ *
++ * int ep93xx_dma_is_done(int handle)
++ *
++ * Description: Determines if the specified channel is done
++ * transferring the requested data.
++ *
++ * handle: handle for the channel to query.
++ *
++ ****************************************************************************/
++int
++ep93xx_dma_is_done(int handle)
++{
++ ep93xx_dma_t *dma;
++ int channel;
++
++ /*
++ * Get the DMA hw channel # from the handle.
++ */
++ channel = dma_get_channel_from_handle(handle);
++
++ /*
++ * See if this is a valid handle.
++ */
++ if (channel < 0) {
++ printk(KERN_ERR "ep93xx_dma_is_done: Invalid dma handle.\n");
++ return(-EINVAL);
++ }
++
++ /*
++ * Get a pointer to the DMA channel state structure.
++ */
++ dma = &dma_chan[channel];
++
++ /*
++ * See if there are any buffers remaining to be provided to the HW.
++ */
++ if (dma->new_buffers)
++ return 0;
++
++ /*
++ * See if this is a M2P or M2M channel.
++ */
++ if (channel < 10) {
++ /*
++ * If the bytes remaining register of the HW is not zero, then
++ * there is more work to be done.
++ */
++ if (inl(dma->reg_base + M2P_OFFSET_REMAIN) != 0)
++ return 0;
++ } else {
++ /*
++ * If either byte count register in the HW is not zero, then there
++ * is more work to be done.
++ */
++ if ((inl(dma->reg_base + M2M_OFFSET_BCR0) != 0) ||
++ (inl(dma->reg_base + M2M_OFFSET_BCR1) != 0))
++ return 0;
++ }
++
++ /*
++ * The DMA is complete.
++ */
++ return 1;
++}
++
++/*****************************************************************************
++ * ep93xx_dma_request
++ *
++ * Description: This function will allocate a DMA channel for a particular
++ * hardware peripheral. Before initiating a transfer on the allocated
++ * channel, the channel must be set up and buffers have to queued up.
++ *
++ * handle: pointer to an integer which is filled in with a unique
++ * handle for this instance of the dma interface.
++ * device_id string with the device name, primarily used by /proc.
++ * device hardware device ID for which the requested dma channel will
++ * transfer data.
++ *
++ ****************************************************************************/
++int
++ep93xx_dma_request(int * handle, const char *device_id,
++ ep93xx_dma_dev_t device)
++{
++ ep93xx_dma_t *dma = NULL;
++ int channel;
++ unsigned int error = 0;
++ unsigned int loop;
++ unsigned int M2P_reg_base;
++
++ /*
++ * Check if the device requesting a DMA channel is a valid device.
++ */
++ if ((device >= UNDEF_DMA) || (device < 0))
++ return(-ENODEV);
++
++ /*
++ * We've got a valid hardware device requesting a DMA channel.
++ * Now check if the device should open an M2P or M2M channel
++ */
++ if (device < 20)
++ channel = dma_open_m2p(device);
++ else
++ channel = dma_open_m2m(device);
++
++ /*
++ * Check if we successfully opened a DMA channel
++ */
++ if (channel < 0) {
++ printk(KERN_ERR "%s: Could not open dma channel for this device.\n",
++ device_id);
++ return(-EBUSY);
++ }
++
++ dma = &dma_chan[channel];
++
++ if(dma->terminated==1) {
++ free_irq(dma->irq, (void *) dma);
++ dma->terminated=0;
++ }
++
++ /*
++ * Request the appropriate IRQ for the specified channel
++ */
++ if (channel < 10)
++ error = request_irq(dma->irq, dma_m2p_irq_handler,
++ IRQF_DISABLED, device_id, (void *) dma);
++ else
++ error = request_irq(dma->irq, &dma_m2m_irq_handler,
++ IRQF_DISABLED, device_id, (void *) dma);
++
++ /*
++ * Check for any errors during the irq request
++ */
++ if (error) {
++ printk(KERN_ERR "%s: unable to request IRQ %d for DMA channel\n",
++ device_id, dma->irq);
++ return(error);
++ }
++
++ /*
++ * Generate a valid handle and exit.
++ *
++ * Increment the last valid handle.
++ * Check for wraparound (unlikely, but we like to be complete).
++ */
++ dma->last_valid_handle++;
++
++ if ( (dma->last_valid_handle & DMA_HANDLE_SPECIFIER_MASK) !=
++ (channel << 28) )
++ dma->last_valid_handle = (channel << 28) + 1;
++
++ /*
++ * Fill in the handle pointer with a valid handle for
++ * this dma channel instance.
++ */
++ *handle = dma->last_valid_handle;
++
++ DPRINTK("Handle for channel %d: 0x%x\n", channel, *handle);
++
++ /*
++ * Save the device ID and device name.
++ */
++ dma->device = device;
++ dma->device_id = device_id;
++
++ /*
++ * Init all fields within the dma instance.
++ */
++ for (loop = 0; loop < MAX_EP93XX_DMA_BUFFERS; loop++)
++ dma->buffer_queue[loop].buf_id = -1;
++
++ /*
++ * Initialize all buffer queue variables.
++ */
++ dma->current_buffer = 0;
++ dma->last_buffer = 0;
++
++ dma->new_buffers = 0;
++ dma->used_buffers = 0;
++ dma->total_buffers = 0;
++
++ /*
++ * Initialize the total bytes variable
++ */
++ dma->total_bytes = 0;
++
++ /*
++ * Initialize the transfer and pause state variables to 0.
++ */
++ dma->xfer_enable = 0;
++
++ dma->pause = 0;
++
++ /*
++ * Initialize the pause buffer structure.
++ */
++ dma->pause_buf.buf_id = -1;
++
++ /*
++ * Initialize the callback function and user data fields.
++ */
++ dma->callback = NULL;
++
++ /*
++ * User data used as a parameter for the Callback function. The user
++ * sets up the data and sends it with the callback function.
++ */
++ dma->user_data = 0;
++
++ M2P_reg_base = dma_chan[channel].reg_base;
++
++ /*
++ * Debugging message.
++ */
++ DPRINTK("Successfully requested dma channel %d\n", channel);
++ DPRINTK("STATUS - 0x%x \n", inl(M2P_reg_base+M2P_OFFSET_STATUS) );
++ DPRINTK("CONTROL - 0x%x \n", inl(M2P_reg_base+M2P_OFFSET_CONTROL) );
++ DPRINTK("REMAIN - 0x%x \n", inl(M2P_reg_base+M2P_OFFSET_REMAIN) );
++ DPRINTK("PPALLOC - 0x%x \n", inl(M2P_reg_base+M2P_OFFSET_PPALLOC) );
++ DPRINTK("BASE0 - 0x%x \n", inl(M2P_reg_base+M2P_OFFSET_BASE0) );
++ DPRINTK("MAXCNT0 - 0x%x \n", inl(M2P_reg_base+M2P_OFFSET_MAXCNT0) );
++ DPRINTK("CURRENT0 - 0x%x \n", inl(M2P_reg_base+M2P_OFFSET_CURRENT0) );
++ DPRINTK("BASE1 - 0x%x \n", inl(M2P_reg_base+M2P_OFFSET_BASE1) );
++ DPRINTK("MAXCNT1 - 0x%x \n", inl(M2P_reg_base+M2P_OFFSET_MAXCNT1) );
++ DPRINTK("CURRENT1 - 0x%x \n", inl(M2P_reg_base+M2P_OFFSET_CURRENT1) );
++
++ DPRINTK("Buffer source size last used \n");
++ for (loop = 0; loop < 5; loop ++)
++ DPRINTK("%d 0x%x 0x%x %d %d \n",
++ loop, dma->buffer_queue[loop].source, dma->buffer_queue[loop].size,
++ dma->buffer_queue[loop].last, dma->buffer_queue[loop].used);
++ DPRINTK("pause 0x%x 0x%x %d %d \n",
++ dma->pause_buf.source, dma->pause_buf.size,
++ dma->pause_buf.last, dma->pause_buf.used);
++
++ DPRINTK("Pause - %d \n", dma->pause);
++ DPRINTK("xfer_enable - %d \n", dma->xfer_enable);
++ DPRINTK("total bytes - 0x%x \n", dma->total_bytes);
++ DPRINTK("total buffer - %d \n", dma->total_buffers);
++ DPRINTK("new buffers - %d \n", dma->new_buffers);
++ DPRINTK("current buffer - %d \n", dma->current_buffer);
++ DPRINTK("last buffer - %d \n", dma->last_buffer);
++ DPRINTK("used buffers - %d \n", dma->used_buffers);
++
++ DPRINTK("CURRENT1 - 0x%x \n", inl(M2P_reg_base+M2P_OFFSET_CURRENT1) );
++ DPRINTK("VIC0IRQSTATUS - 0x%x, VIC0INTENABLE - 0x%x \n",
++ *(unsigned int *)(VIC0IRQSTATUS),
++ *(unsigned int *)(VIC0INTENABLE));
++
++ /*
++ * Success.
++ */
++ return(0);
++}
++
++/*****************************************************************************
++ *
++ * ep93xx_dma_free
++ *
++ * Description: This function will free the dma channel for future requests.
++ *
++ * handle: handle for the channel to be freed.
++ *
++ ****************************************************************************/
++int
++ep93xx_dma_free(int handle)
++{
++ ep93xx_dma_t *dma;
++ unsigned int M2M_reg_base, M2P_reg_base, uiCONTROL;
++ int channel;
++
++ /*
++ * Get the DMA hw channel # from the handle.
++ */
++ channel = dma_get_channel_from_handle(handle);
++
++ /*
++ * See if this is a valid handle.
++ */
++ if (channel < 0) {
++ printk(KERN_ERR "DMA Free: Invalid dma handle.\n");
++ return(-EINVAL);
++ }
++
++ /*
++ * Get a pointer to the dma instance.
++ */
++ dma = &dma_chan[channel];
++
++ /*
++ * Disable the dma channel
++ */
++ if (channel < 10) {
++ /*
++ * M2P channel
++ */
++ M2P_reg_base = dma->reg_base;
++
++ uiCONTROL = inl(M2P_reg_base+M2P_OFFSET_CONTROL);
++ uiCONTROL &= ~CONTROL_M2P_ENABLE;
++ outl( uiCONTROL, M2P_reg_base+M2P_OFFSET_CONTROL );
++ } else {
++ /*
++ * M2M channel
++ */
++ M2M_reg_base = dma->reg_base;
++
++ uiCONTROL = inl(M2M_reg_base+M2M_OFFSET_CONTROL);
++ uiCONTROL &= ~CONTROL_M2M_ENABLE;
++ outl( uiCONTROL, M2M_reg_base+M2M_OFFSET_CONTROL );
++ }
++
++ /*
++ * Free the interrupt servicing this dma channel
++ */
++ //free_irq(dma->irq, (void *) dma);
++ dma->terminated=1;
++
++ /*
++ * Decrement the reference count for this instance of the dma interface
++ */
++ dma->ref_count--;
++
++ /*
++ * Set the transfer and pause state variables to 0
++ * (unititialized state).
++ */
++ dma->xfer_enable = 0;
++ dma->pause = 0;
++
++ /*
++ * Debugging message.
++ */
++ DPRINTK("Successfully freed dma channel %d\n", channel);
++ /*
++ * Success.
++ */
++ return(0);
++}
++
++/*****************************************************************************
++ *
++ * ep93xx_dma_init(void)
++ *
++ * Description: This function is called during system initialization to
++ * setup the interrupt number and register set base address for each DMA
++ * channel.
++ *
++ ****************************************************************************/
++static int __init
++ep93xx_dma_init(void)
++{
++ int channel;
++
++ /*
++ * Init some values in each dma instance.
++ */
++ for (channel = 0; channel < MAX_EP93XX_DMA_CHANNELS; channel++) {
++ /*
++ * IRQ for the specified dma channel.
++ */
++ dma_chan[channel].irq = IRQ_EP93XX_DMAM2P0 + channel;
++
++ dma_chan[channel].terminated = 0;
++
++ /*
++ * Initial value of the dma channel handle.
++ */
++ dma_chan[channel].last_valid_handle = channel << 28;
++
++ /*
++ * Give the instance a pointer to the dma channel register
++ * base.
++ */
++ if (channel < 10)
++ dma_chan[channel].reg_base = DMAM2PChannelBase[channel];
++ else
++ dma_chan[channel].reg_base = DMAM2MChannelBase[channel - 10];
++
++ /*
++ * Initialize the reference count for this channel.
++ */
++ dma_chan[channel].ref_count = 0;
++ }
++
++ DPRINTK("DMA Interface intitialization complete\n");
++
++ /*
++ * Success
++ */
++ return 0;
++}
++
++arch_initcall(ep93xx_dma_init);
++
++EXPORT_SYMBOL(ep93xx_dma_free);
++EXPORT_SYMBOL(ep93xx_dma_request);
++EXPORT_SYMBOL(ep93xx_dma_flush);
++EXPORT_SYMBOL(ep93xx_dma_pause);
++EXPORT_SYMBOL(ep93xx_dma_remove_buffer);
++EXPORT_SYMBOL(ep93xx_dma_add_buffer);
++EXPORT_SYMBOL(ep93xx_dma_start);
++EXPORT_SYMBOL(ep93xx_dma_config);
+--- /dev/null
++++ b/arch/arm/mach-ep93xx/dma_ep93xx.h
+@@ -0,0 +1,676 @@
++/*****************************************************************************
++ *
++ * arch/arm/mach-ep93xx/dma_ep93xx.h
++ *
++ * DESCRIPTION: 93XX DMA controller API private defintions.
++ *
++ * Copyright Cirrus Logic Corporation, 2003. All rights reserved
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ *
++ ****************************************************************************/
++#ifndef _EP93XX_DMA_H_
++#define _EP93XX_DMA_H_
++
++// as it turns out the ide dma is the biggest dma buffer hog so far
++// in case the HDD is "thinking" (seek/buffer flush)
++// the continueing r/w DMAs to the HDD will be queued up to up to PRD_ENTRIES entries...
++#include <linux/ide.h>
++#define MAX_EP93XX_DMA_BUFFERS PRD_ENTRIES
++
++#ifndef TRUE
++#define TRUE 1
++#endif
++
++#ifndef FALSE
++#define FALSE 0
++#endif
++
++#ifndef NULL
++#define NULL 0
++#endif
++
++#define EP93XX_DMA_BASE (EP93XX_AHB_VIRT_BASE + 0x00000000)
++
++/*****************************************************************************
++ * 0x8000.0000 -> 0x8000.003C M2P Channel 0 Registers (Tx)
++ * 0x8000.0040 -> 0x8000.007C M2P Channel 1 Registers (Rx)
++ * 0x8000.0080 -> 0x8000.00BC M2P Channel 2 Registers (Tx)
++ * 0x8000.00C0 -> 0x8000.00FC M2P Channel 3 Registers (Rx)
++ * 0x8000.0100 -> 0x8000.013C M2M Channel 0 Registers
++ * 0x8000.0140 -> 0x8000.017C M2M Channel 1 Registers
++ * 0x8000.0180 -> 0x8000.01BC Not Used
++ * 0x8000.01C0 -> 0x8000.01FC Not Used
++ * 0x8000.0200 -> 0x8000.023C M2P Channel 5 Registers (Rx)
++ * 0x8000.0240 -> 0x8000.027C M2P Channel 4 Registers (Tx)
++ * 0x8000.0280 -> 0x8000.02BC M2P Channel 7 Registers (Rx)
++ * 0x8000.02C0 -> 0x8000.02FC M2P Channel 6 Registers (Tx)
++ * 0x8000.0300 -> 0x8000.033C M2P Channel 9 Registers (Rx)
++ * 0x8000.0340 -> 0x8000.037C M2P Channel 8 Registers (Tx)
++ * 0x8000.0380 DMA Channel Arbitration register
++ * 0x8000.03C0 DMA Global Interrupt register
++ * 0x8000.03C4 -> 0x8000.03FC Not Used
++ *
++ *
++ * Internal M2P/P2M Channel Register Map
++ *
++ * Offset Name Access Bits Reset Value
++ * 0x00 CONTROL R/W 6 0
++ * 0x04 INTERRUPT R/W TC* 3 0
++ * 0x08 PPALLOC R/W 4 channel dependant
++ * (see reg description)
++ * 0x0C STATUS RO 8 0
++ * 0x10 reserved
++ * 0x14 REMAIN RO 16 0
++ * 0X18 Reserved
++ * 0X1C Reserved
++ * 0x20 MAXCNT0 R/W 16 0
++ * 0x24 BASE0 R/W 32 0
++ * 0x28 CURRENT0 RO 32 0
++ * 0x2C Reserved
++ * 0x30 MAXCNT1 R/W 16 0
++ * 0x34 BASE1 R/W 32 0
++ * 0X38 CURRENT1 RO 32 0
++ * 0X3C Reserved
++ *
++ * M2M Channel Register Map
++ * Offset Name Access Bits Reset Value
++ *
++ * 0x00 CONTROL R/W 22 0
++ * 0x04 INTERRUPT R/W TC* 3 0
++ * 0x08 Reserved
++ * 0x0C STATUS R/W TC* 14 0
++ * 0x10 BCR0 R/W 16 0
++ * 0x14 BCR1 R/W 16 0
++ * 0x18 SAR_BASE0 R/W 32 0
++ * 0x1C SAR_BASE1 R/W 32 0
++ * 0x20 Reserved
++ * 0x24 SAR_CURRENT0 RO 32 0
++ * 0x28 SAR_CURRENT1 RO 32 0
++ * 0x2C DAR_BASE0 R/W 32 0
++ * 0x30 DAR_BASE1 R/W 32 0
++ * 0x34 DAR_CURRENT0 RO 32 0
++ * 0X38 Reserved
++ * 0X3C DAR_CURRENT1 RO 32 0
++ * * Write this location once to clear the bit (see
++ * Interrupt/Status register description for which bits
++ * this rule applies to).
++ *
++ ****************************************************************************/
++
++
++/*----------------------------------------------------------------------------------*/
++/* M2P Registers */
++/*----------------------------------------------------------------------------------*/
++/*
++ * M2P CONTROL register bit defines
++ */
++#define CONTROL_M2P_STALLINTEN 0x00000001 /* Enables the STALL interrupt */
++#define CONTROL_M2P_NFBINTEN 0x00000002 /* Enables the NFB interrupt */
++#define CONTROL_M2P_CHERRORINTEN 0x00000008 /* Enables the ChError interrupt*/
++#define CONTROL_M2P_ENABLE 0x00000010 /* Enables the channel */
++#define CONTROL_M2P_ABRT 0x00000020 /* Determines how DMA behaves in*/
++ /* NEXT state with peripheral */
++ /* error */
++ /* 0: NEXT -> ON, ignore error */
++ /* 1: NEXT -> STALL, disable ch.*/
++#define CONTROL_M2P_ICE 0x00000040 /* Ignore Channel Error */
++
++/*
++ * M2P INTERRUPT register bit defines
++ */
++#define INTERRUPT_M2P_STALLINT 0x00000001 /* Indicates channel stalled. */
++#define INTERRUPT_M2P_NFBINT 0x00000002 /* Indicates channel is hungry. */
++#define INTERRUPT_M2P_CHERRORINT 0x00000008 /* Peripheral detects error */
++
++
++/*
++ * STATUS register bit defines
++ */
++#define STATUS_M2P_STALL 0x00000001 /* A '1' indicates channel is */
++ /* stalled */
++#define STATUS_M2P_NFB 0x00000002 /* A '1' indicates channel has moved*/
++ /* from NEXT state to ON state, but */
++ /* waiting for next buffer to be */
++ /* programmed. */
++#define STATUS_M2P_CHERROR 0x00000008 /* Enables the ChError interrupt */
++#define STATUS_M2P_CURRENT_MASK 0x00000030 /* Current state of the FSM */
++#define STATUS_M2P_CURRENT_SHIFT 4
++#define STATUS_M2P_NEXTBUFFER 0x00000040 /* Informs the int handler after an */
++ /* NFB int which pair of maxcnt and */
++ /* base regs to update. */
++#define STATUS_M2P_BYTES_MASK 0x0000f800 /* number of valid DMA data */
++#define STATUS_M2P_BYTES_SHIFT 7 /* currently in */
++ /* packer/unpacker */
++
++#define STATUS_M2P_DMA_NO_BUF 0x00000000
++#define STATUS_M2P_DMA_BUF_ON 0x00000010
++#define STATUS_M2P_DMA_BUF_NEXT 0x00000020
++
++/*
++ * Register masks to mask off reserved bits after reading register.
++ */
++#define M2P_MASK_PPALLOC 0x0000000f
++#define M2P_MASK_REMAIN 0x0000ffff
++#define M2P_MASK_MAXCNT0 0x0000ffff
++#define M2P_MASK_BASE0 0xffffffff
++#define M2P_MASK_CURRENT0 0xffffffff
++#define M2P_MASK_MAXCNT1 0x0000ffff
++#define M2P_MASK_BASE1 0xffffffff
++#define M2P_MASK_CURRENT1 0xffffffff
++
++
++/*----------------------------------------------------------------------------------*/
++/* M2M Registers */
++/*----------------------------------------------------------------------------------*/
++
++#define CONTROL_M2M_STALLINTEN 0x00000001 /* Enables the STALL interrupt */
++#define CONTROL_M2M_SCT 0x00000002 /* Source Copy Transfer. Setup a */
++ /* block transfer from 1 memory source */
++ /* location. */
++#define CONTROL_M2M_DONEINTEN 0x00000004 /* Enables the DONE interrupt which */
++ /* indicates if the xfer completed */
++ /* successfully */
++#define CONTROL_M2M_ENABLE 0x00000008 /* Enables the channel */
++#define CONTROL_M2M_START 0x00000010 /* Initiates the xfer. 'software trigger' */
++#define CONTROL_M2M_BWC_MASK 0x000001e0 /* Bandwidth control. Indicate number of */
++#define CONTROL_M2M_BWC_SHIFT 5 /* bytes in a transfer. */
++#define CONTROL_M2M_PW_MASK 0x00000600 /* Peripheral width. Used for xfers */
++#define CONTROL_M2M_PW_SHIFT 9 /* between memory and external peripheral. */
++ /* 00: byte, 01: halfword, 10: word. */
++#define CONTROL_M2M_DAH 0x00000800 /* Destination Address Hold */
++#define CONTROL_M2M_SAH 0x00001000 /* Source Address Hold */
++#define CONTROL_M2M_TM_MASK 0x00006000 /* Transfer Mode. 00: sw triggered, */
++#define CONTROL_M2M_TM_SHIFT 13 /* 01: hw initiated M2P, 01: hw initiated P2M */
++#define CONTROL_M2M_ETDP_MASK 0x00018000 /* End-of-Transfer/Terminal Count pin */
++#define CONTROL_M2M_ETDP_SHIFT 15 /* direction and polarity. */
++#define CONTROL_M2M_DACKP 0x00020000 /* DMA acknowledge pin polarity */
++
++#define CONTROL_M2M_DREQP_MASK 0x00180000 /* DMA request pin polarity. must be set */
++#define CONTROL_M2M_DREQP_SHIFT 19 /* before enable bit. */
++#define CONTROL_M2M_NFBINTEN 0x00200000 /* Enables generation of the NFB interrupt. */
++#define CONTROL_M2M_RSS_MASK 0x00c00000 /* Request source selection: */
++#define CONTROL_M2M_RSS_SHIFT 22 /* 000 - External DReq[0] */
++ /* 001 - External DReq[1] */
++ /* 01X - Internal SSPRx */
++ /* 10X - Internal SSPTx */
++ /* 11X - Internal IDE */
++#define CONTROL_M2M_NO_HDSK 0x01000000 /* No handshake. When set the peripheral doesn't */
++ /* require the regular handshake protocal. Must */
++ /* be set for SSP and IDE operations, optional */
++ /* for external peripherals. */
++#define CONTROL_M2M_PWSC_MASK 0xfe000000 /* Peripheral wait states count. Gives the latency */
++#define CONTROL_M2M_PWSC_SHIFT 25 /* (in PCLK cycles) needed by the peripheral to */
++ /* deassert its' request once the M2M xfer w/ DMA */
++ /* is complete. */
++
++/*
++ * M2M INTERRUPT register bit defines
++ */
++#define INTERRUPT_M2M_STALLINT 0x00000001 /* Stall interrupt indicates channel stalled. */
++#define INTERRUPT_M2M_DONEINT 0x00000002 /* Transaction done. */
++#define INTERRUPT_M2M_NFBINT 0x00000004 /* Next frame buffer interrupt indicates */
++ /* channel requires a new buffer */
++
++
++
++/*
++ * M2M STATUS register bit defines
++ */
++#define STATUS_M2M_STALL 0x00000001 /* A '1' indicates channel is stalled */
++#define STATUS_M2M_CURRENTSTATE_MASK 0x0000003e /* Indicates state of M2M Channel control */
++#define STATUS_M2M_CURRENTSTATE_SHIFT 1 /* FSM (0-2): */
++ /* 000 - IDLE, 001 - STALL, 010 - MEM_RD, */
++ /* 011 - MEM_WR, 100 - BWC_WAIT */
++ /* and M2M buffer FSM (3-2): */
++ /* 00 - NO_BUF, 01 - BUF_ON, 10 - BUF_NEXT */
++#define STATUS_M2M_DONE 0x00000040 /* Transfer completed successfully if 1. */
++#define STATUS_M2M_TCS_MASK 0x00000180 /* Terminal Count status. Indicates whether or */
++#define STATUS_M2M_TCS_SHIFT 7 /* or not the actual byte count reached */
++ /* programmed limit for buffer descriptor */
++#define STATUS_M2M_EOTS_MASK 0x00000600 /* End-of-Transfer status for buffer */
++#define STATUS_M2M_EOTS_SHIFT 9
++#define STATUS_M2M_NFB 0x00000800 /* A '1' indicates channel has moved */
++ /* from NEXT state to ON state, but the next */
++ /* byte count reg for next buffer has not been */
++ /* programmed yet. */
++#define STATUS_M2M_NB 0x00001000 /* NextBuffer status. Informs NFB service */
++ /* routine, after NFB int, which pair of buffer */
++ /* descriptor registers is free to update. */
++#define STATUS_M2M_DREQS 0x00002000 /* DREQ status. Reflects the status of the */
++ /* synchronized external peripherals DMA */
++ /* request signal. */
++
++/*
++ * Register masks to mask off reserved bits after reading register.
++ */
++#define M2M_MASK_BCR0 0x0000ffff
++#define M2M_MASK_BCR1 0x0000ffff
++#define M2M_MASK_SAR_BASE0 0xffffffff
++#define M2M_MASK_SAR_BASE1 0xffffffff
++#define M2M_MASK_SAR_CURRENT0 0xffffffff
++#define M2M_MASK_SAR_CURRENT1 0xffffffff
++#define M2M_MASK_DAR_BASE0 0xffffffff
++#define M2M_MASK_DAR_BASE1 0xffffffff
++#define M2M_MASK_DAR_CURRENT0 0xffffffff
++#define M2M_MASK_DAR_CURRENT1 0xffffffff
++
++
++//
++/* 8000_0000 - 8000_ffff: DMA */
++#define DMA_OFFSET 0x000000
++#define DMA_BASE (EP93XX_DMA_BASE)
++#define DMAMP_TX_0_CONTROL (DMA_BASE+0x0000)
++#define DMAMP_TX_0_INTERRUPT (DMA_BASE+0x0004)
++#define DMAMP_TX_0_PPALLOC (DMA_BASE+0x0008)
++#define DMAMP_TX_0_STATUS (DMA_BASE+0x000C)
++#define DMAMP_TX_0_REMAIN (DMA_BASE+0x0014)
++#define DMAMP_TX_0_MAXCNT0 (DMA_BASE+0x0020)
++#define DMAMP_TX_0_BASE0 (DMA_BASE+0x0024)
++#define DMAMP_TX_0_CURRENT0 (DMA_BASE+0x0028)
++#define DMAMP_TX_0_MAXCNT1 (DMA_BASE+0x0030)
++#define DMAMP_TX_0_BASE1 (DMA_BASE+0x0034)
++#define DMAMP_TX_0_CURRENT1 (DMA_BASE+0x0038)
++
++#define DMAMP_RX_1_CONTROL (DMA_BASE+0x0040)
++#define DMAMP_RX_1_INTERRUPT (DMA_BASE+0x0044)
++#define DMAMP_RX_1_PPALLOC (DMA_BASE+0x0048)
++#define DMAMP_RX_1_STATUS (DMA_BASE+0x004C)
++#define DMAMP_RX_1_REMAIN (DMA_BASE+0x0054)
++#define DMAMP_RX_1_MAXCNT0 (DMA_BASE+0x0060)
++#define DMAMP_RX_1_BASE0 (DMA_BASE+0x0064)
++#define DMAMP_RX_1_CURRENT0 (DMA_BASE+0x0068)
++#define DMAMP_RX_1_MAXCNT1 (DMA_BASE+0x0070)
++#define DMAMP_RX_1_BASE1 (DMA_BASE+0x0074)
++#define DMAMP_RX_1_CURRENT1 (DMA_BASE+0x0078)
++
++#define DMAMP_TX_2_CONTROL (DMA_BASE+0x0080)
++#define DMAMP_TX_2_INTERRUPT (DMA_BASE+0x0084)
++#define DMAMP_TX_2_PPALLOC (DMA_BASE+0x0088)
++#define DMAMP_TX_2_STATUS (DMA_BASE+0x008C)
++#define DMAMP_TX_2_REMAIN (DMA_BASE+0x0094)
++#define DMAMP_TX_2_MAXCNT0 (DMA_BASE+0x00A0)
++#define DMAMP_TX_2_BASE0 (DMA_BASE+0x00A4)
++#define DMAMP_TX_2_CURRENT0 (DMA_BASE+0x00A8)
++#define DMAMP_TX_2_MAXCNT1 (DMA_BASE+0x00B0)
++#define DMAMP_TX_2_BASE1 (DMA_BASE+0x00B4)
++#define DMAMP_TX_2_CURRENT1 (DMA_BASE+0x00B8)
++
++#define DMAMP_RX_3_CONTROL (DMA_BASE+0x00C0)
++#define DMAMP_RX_3_INTERRUPT (DMA_BASE+0x00C4)
++#define DMAMP_RX_3_PPALLOC (DMA_BASE+0x00C8)
++#define DMAMP_RX_3_STATUS (DMA_BASE+0x00CC)
++#define DMAMP_RX_3_REMAIN (DMA_BASE+0x00D4)
++#define DMAMP_RX_3_MAXCNT0 (DMA_BASE+0x00E0)
++#define DMAMP_RX_3_BASE0 (DMA_BASE+0x00E4)
++#define DMAMP_RX_3_CURRENT0 (DMA_BASE+0x00E8)
++#define DMAMP_RX_3_MAXCNT1 (DMA_BASE+0x00F0)
++#define DMAMP_RX_3_BASE1 (DMA_BASE+0x00F4)
++#define DMAMP_RX_3_CURRENT1 (DMA_BASE+0x00F8)
++
++#define DMAMM_0_CONTROL (DMA_BASE+0x0100)
++#define DMAMM_0_INTERRUPT (DMA_BASE+0x0104)
++#define DMAMM_0_STATUS (DMA_BASE+0x010C)
++#define DMAMM_0_BCR0 (DMA_BASE+0x0110)
++#define DMAMM_0_BCR1 (DMA_BASE+0x0114)
++#define DMAMM_0_SAR_BASE0 (DMA_BASE+0x0118)
++#define DMAMM_0_SAR_BASE1 (DMA_BASE+0x011C)
++#define DMAMM_0_SAR_CURRENT0 (DMA_BASE+0x0124)
++#define DMAMM_0_SAR_CURRENT1 (DMA_BASE+0x0128)
++#define DMAMM_0_DAR_BASE0 (DMA_BASE+0x012C)
++#define DMAMM_0_DAR_BASE1 (DMA_BASE+0x0130)
++#define DMAMM_0_DAR_CURRENT0 (DMA_BASE+0x0134)
++#define DMAMM_0_DAR_CURRENT1 (DMA_BASE+0x013C)
++
++#define DMAMM_1_CONTROL (DMA_BASE+0x0140)
++#define DMAMM_1_INTERRUPT (DMA_BASE+0x0144)
++#define DMAMM_1_STATUS (DMA_BASE+0x014C)
++#define DMAMM_1_BCR0 (DMA_BASE+0x0150)
++#define DMAMM_1_BCR1 (DMA_BASE+0x0154)
++#define DMAMM_1_SAR_BASE0 (DMA_BASE+0x0158)
++#define DMAMM_1_SAR_BASE1 (DMA_BASE+0x015C)
++#define DMAMM_1_SAR_CURRENT0 (DMA_BASE+0x0164)
++#define DMAMM_1_SAR_CURRENT1 (DMA_BASE+0x0168)
++#define DMAMM_1_DAR_BASE0 (DMA_BASE+0x016C)
++#define DMAMM_1_DAR_BASE1 (DMA_BASE+0x0170)
++#define DMAMM_1_DAR_CURRENT0 (DMA_BASE+0x0174)
++#define DMAMM_1_DAR_CURRENT1 (DMA_BASE+0x017C)
++
++#define DMAMP_RX_5_CONTROL (DMA_BASE+0x0200)
++#define DMAMP_RX_5_INTERRUPT (DMA_BASE+0x0204)
++#define DMAMP_RX_5_PPALLOC (DMA_BASE+0x0208)
++#define DMAMP_RX_5_STATUS (DMA_BASE+0x020C)
++#define DMAMP_RX_5_REMAIN (DMA_BASE+0x0214)
++#define DMAMP_RX_5_MAXCNT0 (DMA_BASE+0x0220)
++#define DMAMP_RX_5_BASE0 (DMA_BASE+0x0224)
++#define DMAMP_RX_5_CURRENT0 (DMA_BASE+0x0228)
++#define DMAMP_RX_5_MAXCNT1 (DMA_BASE+0x0230)
++#define DMAMP_RX_5_BASE1 (DMA_BASE+0x0234)
++#define DMAMP_RX_5_CURRENT1 (DMA_BASE+0x0238)
++
++#define DMAMP_TX_4_CONTROL (DMA_BASE+0x0240)
++#define DMAMP_TX_4_INTERRUPT (DMA_BASE+0x0244)
++#define DMAMP_TX_4_PPALLOC (DMA_BASE+0x0248)
++#define DMAMP_TX_4_STATUS (DMA_BASE+0x024C)
++#define DMAMP_TX_4_REMAIN (DMA_BASE+0x0254)
++#define DMAMP_TX_4_MAXCNT0 (DMA_BASE+0x0260)
++#define DMAMP_TX_4_BASE0 (DMA_BASE+0x0264)
++#define DMAMP_TX_4_CURRENT0 (DMA_BASE+0x0268)
++#define DMAMP_TX_4_MAXCNT1 (DMA_BASE+0x0270)
++#define DMAMP_TX_4_BASE1 (DMA_BASE+0x0274)
++#define DMAMP_TX_4_CURRENT1 (DMA_BASE+0x0278)
++
++#define DMAMP_RX_7_CONTROL (DMA_BASE+0x0280)
++#define DMAMP_RX_7_INTERRUPT (DMA_BASE+0x0284)
++#define DMAMP_RX_7_PPALLOC (DMA_BASE+0x0288)
++#define DMAMP_RX_7_STATUS (DMA_BASE+0x028C)
++#define DMAMP_RX_7_REMAIN (DMA_BASE+0x0294)
++#define DMAMP_RX_7_MAXCNT0 (DMA_BASE+0x02A0)
++#define DMAMP_RX_7_BASE0 (DMA_BASE+0x02A4)
++#define DMAMP_RX_7_CURRENT0 (DMA_BASE+0x02A8)
++#define DMAMP_RX_7_MAXCNT1 (DMA_BASE+0x02B0)
++#define DMAMP_RX_7_BASE1 (DMA_BASE+0x02B4)
++#define DMAMP_RX_7_CURRENT1 (DMA_BASE+0x02B8)
++
++#define DMAMP_TX_6_CONTROL (DMA_BASE+0x02C0)
++#define DMAMP_TX_6_INTERRUPT (DMA_BASE+0x02C4)
++#define DMAMP_TX_6_PPALLOC (DMA_BASE+0x02C8)
++#define DMAMP_TX_6_STATUS (DMA_BASE+0x02CC)
++#define DMAMP_TX_6_REMAIN (DMA_BASE+0x02D4)
++#define DMAMP_TX_6_MAXCNT0 (DMA_BASE+0x02E0)
++#define DMAMP_TX_6_BASE0 (DMA_BASE+0x02E4)
++#define DMAMP_TX_6_CURRENT0 (DMA_BASE+0x02E8)
++#define DMAMP_TX_6_MAXCNT1 (DMA_BASE+0x02F0)
++#define DMAMP_TX_6_BASE1 (DMA_BASE+0x02F4)
++#define DMAMP_TX_6_CURRENT1 (DMA_BASE+0x02F8)
++
++#define DMAMP_RX_9_CONTROL (DMA_BASE+0x0300)
++#define DMAMP_RX_9_INTERRUPT (DMA_BASE+0x0304)
++#define DMAMP_RX_9_PPALLOC (DMA_BASE+0x0308)
++#define DMAMP_RX_9_STATUS (DMA_BASE+0x030C)
++#define DMAMP_RX_9_REMAIN (DMA_BASE+0x0314)
++#define DMAMP_RX_9_MAXCNT0 (DMA_BASE+0x0320)
++#define DMAMP_RX_9_BASE0 (DMA_BASE+0x0324)
++#define DMAMP_RX_9_CURRENT0 (DMA_BASE+0x0328)
++#define DMAMP_RX_9_MAXCNT1 (DMA_BASE+0x0330)
++#define DMAMP_RX_9_BASE1 (DMA_BASE+0x0334)
++#define DMAMP_RX_9_CURRENT1 (DMA_BASE+0x0338)
++
++#define DMAMP_TX_8_CONTROL (DMA_BASE+0x0340)
++#define DMAMP_TX_8_INTERRUPT (DMA_BASE+0x0344)
++#define DMAMP_TX_8_PPALLOC (DMA_BASE+0x0348)
++#define DMAMP_TX_8_STATUS (DMA_BASE+0x034C)
++#define DMAMP_TX_8_REMAIN (DMA_BASE+0x0354)
++#define DMAMP_TX_8_MAXCNT0 (DMA_BASE+0x0360)
++#define DMAMP_TX_8_BASE0 (DMA_BASE+0x0364)
++#define DMAMP_TX_8_CURRENT0 (DMA_BASE+0x0368)
++#define DMAMP_TX_8_MAXCNT1 (DMA_BASE+0x0370)
++#define DMAMP_TX_8_BASE1 (DMA_BASE+0x0374)
++#define DMAMP_TX_8_CURRENT1 (DMA_BASE+0x0378)
++
++#define DMA_ARBITRATION (DMA_BASE+0x0380)
++#define DMA_INTERRUPT (DMA_BASE+0x03C0)
++
++
++/*
++ * DMA Register Base addresses and Offsets
++ */
++#define DMA_M2P_TX_0_BASE DMAMP_TX_0_CONTROL
++#define DMA_M2P_RX_1_BASE DMAMP_RX_1_CONTROL
++#define DMA_M2P_TX_2_BASE DMAMP_TX_2_CONTROL
++#define DMA_M2P_RX_3_BASE DMAMP_RX_3_CONTROL
++#define DMA_M2M_0_BASE DMAMM_0_CONTROL
++#define DMA_M2M_1_BASE DMAMM_1_CONTROL
++#define DMA_M2P_RX_5_BASE DMAMP_RX_5_CONTROL
++#define DMA_M2P_TX_4_BASE DMAMP_TX_4_CONTROL
++#define DMA_M2P_RX_7_BASE DMAMP_RX_7_CONTROL
++#define DMA_M2P_TX_6_BASE DMAMP_TX_6_CONTROL
++#define DMA_M2P_RX_9_BASE DMAMP_RX_9_CONTROL
++#define DMA_M2P_TX_8_BASE DMAMP_TX_8_CONTROL
++
++#define M2P_OFFSET_CONTROL 0x0000
++#define M2P_OFFSET_INTERRUPT 0x0004
++#define M2P_OFFSET_PPALLOC 0x0008
++#define M2P_OFFSET_STATUS 0x000C
++#define M2P_OFFSET_REMAIN 0x0014
++#define M2P_OFFSET_MAXCNT0 0x0020
++#define M2P_OFFSET_BASE0 0x0024
++#define M2P_OFFSET_CURRENT0 0x0028
++#define M2P_OFFSET_MAXCNT1 0x0030
++#define M2P_OFFSET_BASE1 0x0034
++#define M2P_OFFSET_CURRENT1 0x0038
++
++#define M2M_OFFSET_CONTROL 0x0000
++#define M2M_OFFSET_INTERRUPT 0x0004
++#define M2M_OFFSET_STATUS 0x000C
++#define M2M_OFFSET_BCR0 0x0010
++#define M2M_OFFSET_BCR1 0x0014
++#define M2M_OFFSET_SAR_BASE0 0x0018
++#define M2M_OFFSET_SAR_BASE1 0x001C
++#define M2M_OFFSET_SAR_CURRENT0 0x0024
++#define M2M_OFFSET_SAR_CURRENT1 0x0028
++#define M2M_OFFSET_DAR_BASE0 0x002C
++#define M2M_OFFSET_DAR_BASE1 0x0030
++#define M2M_OFFSET_DAR_CURRENT0 0x0034
++#define M2M_OFFSET_DAR_CURRENT1 0x003C
++
++
++
++//-----------------------------------------------------------------------------
++// PWRCNT Register Defines
++//-----------------------------------------------------------------------------
++#define SYSCON_PWRCNT_FIREN 0x80000000
++#define SYSCON_PWRCNT_UARTBAUD 0x20000000
++#define SYSCON_PWRCNT_USHEN 0x10000000
++#define SYSCON_PWRCNT_DMA_M2MCH1 0x08000000
++#define SYSCON_PWRCNT_DMA_M2MCH0 0x04000000
++#define SYSCON_PWRCNT_DMA_M2PCH8 0x02000000
++#define SYSCON_PWRCNT_DMA_M2PCH9 0x01000000
++#define SYSCON_PWRCNT_DMA_M2PCH6 0x00800000
++#define SYSCON_PWRCNT_DMA_M2PCH7 0x00400000
++#define SYSCON_PWRCNT_DMA_M2PCH4 0x00200000
++#define SYSCON_PWRCNT_DMA_M2PCH5 0x00100000
++#define SYSCON_PWRCNT_DMA_M2PCH2 0x00080000
++#define SYSCON_PWRCNT_DMA_M2PCH3 0x00040000
++#define SYSCON_PWRCNT_DMA_M2PCH0 0x00020000
++#define SYSCON_PWRCNT_DMA_M2PCH1 0x00010000
++
++#ifndef __ASSEMBLY__
++/*
++ * DMA Register Base addresses
++ */
++static unsigned int const DMAM2PChannelBase[10] =
++{
++ DMA_M2P_TX_0_BASE,
++ DMA_M2P_RX_1_BASE,
++ DMA_M2P_TX_2_BASE,
++ DMA_M2P_RX_3_BASE,
++ DMA_M2P_TX_4_BASE,
++ DMA_M2P_RX_5_BASE,
++ DMA_M2P_TX_6_BASE,
++ DMA_M2P_RX_7_BASE,
++ DMA_M2P_TX_8_BASE,
++ DMA_M2P_RX_9_BASE
++};
++
++static unsigned int const DMAM2MChannelBase[2] =
++{
++ DMA_M2M_0_BASE,
++ DMA_M2M_1_BASE
++};
++
++#endif /* __ASSEMBLY__ */
++
++/*****************************************************************************
++ *
++ * DMA buffer structure type.
++ *
++ ****************************************************************************/
++typedef struct ep93xx_dma_buffer_s
++{
++ unsigned int source; /* buffer physical source address. */
++ unsigned int dest; /* buffer physical destination address, */
++ /* only used with the 2 M2M channels. */
++ unsigned int size; /* buffer size in bytes */
++ unsigned int last; /* 1 if this is the last buffer */
++ /* in this transaction. If 1, */
++ /* disable the NFBint so we aren't */
++ /* interrupted for another buffer */
++ /* when we know there won't be another. */
++ unsigned int used; /* This field is set to 1 by the DMA */
++ /* interface after the buffer is transferred*/
++ int buf_id; /* unique identifyer specified by the */
++ /* the driver which requested the dma */
++} ep93xx_dma_buffer_t;
++
++typedef ep93xx_dma_buffer_t * ep93xx_dma_buffer_p;
++
++/*****************************************************************************
++ *
++ * Instance definition for the DMA interface.
++ *
++ ****************************************************************************/
++typedef struct ep9312_dma_s
++{
++ /*
++ * This 1 when the instance is in use, and 0 when it's not.
++ */
++ unsigned int ref_count;
++
++ /*
++ * This is the last valid handle for this instance. When giving out a
++ * new handle this will be incremented and given out.
++ */
++ int last_valid_handle;
++
++ /*
++ * device specifies one of the 20 DMA hardware ports this
++ * DMA channel will service.
++ */
++ ep93xx_dma_dev_t device;
++
++ /*
++ * DMABufferQueue is the queue of buffer structure pointers which the
++ * dma channel will use to setup transfers.
++ */
++ ep93xx_dma_buffer_t buffer_queue[MAX_EP93XX_DMA_BUFFERS];
++
++ /*
++ * currnt_buffer : This is the buffer currently being transfered on
++ * this channel.
++ * last_buffer : This is the last buffer for this transfer.
++ * Note: current_buffer + 1 is already programmed into the dma
++ * channel as the next buffer to transfer. Don't write
++ * over either entry.
++ */
++ int current_buffer;
++ int last_buffer;
++
++ /*
++ * The following 3 fields are buffer counters.
++ *
++ * iNewBuffers: Buffers in the queue which have not been transfered.
++ * iUsedBuffers: Buffers in the queue which have have been tranferred,
++ * and are waiting to be returned.
++ * iTotalBuffers: Total number of buffers in the queue.
++ */
++ int new_buffers;
++ int used_buffers;
++ int total_buffers;
++
++ /*
++ * uiTotalBytes has the total bytes transfered on the channel since the
++ * last flush. This value does not include the bytes tranfered in the
++ * current buffer. A byte count is only added after a complete buffer
++ * is tranfered.
++ */
++ unsigned int total_bytes;
++
++ /*
++ * Interrupt number for this channel
++ */
++ unsigned int irq;
++
++ /*
++ * Indicates whether or not the channel is currently enabled to transfer
++ * data.
++ */
++ unsigned int xfer_enable;
++
++ /*
++ * pause indicates if the dma channel was paused by calling the pause
++ * ioctl.
++ */
++ unsigned int pause;
++
++ /*
++ * buffer structure used during a pause to capture the current
++ * address and remaining bytes for the buffer actively being transferred
++ * on the channel. This buffer will be used to reprogram the dma
++ * channel upon a resume.
++ */
++ ep93xx_dma_buffer_t pause_buf;
++
++ /*
++ * DMACallback is a function pointer which the calling application can
++ * use install a function to. this fuction can be used to notify the
++ * calling application of an interrupt.
++ */
++ dma_callback callback;
++
++ /*
++ * User data used as a parameter for the Callback function. The user
++ * sets up the data and sends it with the callback function.
++ */
++ unsigned int user_data;
++
++ /*
++ * A string representation of the device attached to the channel.
++ */
++ const char * device_id;
++
++ /*
++ * The register base address for this dma channel.
++ */
++ unsigned int reg_base;
++
++ /*
++ * terminated indicates
++ */
++ unsigned int terminated;
++
++
++} ep93xx_dma_t;
++
++/*****************************************************************************
++ *
++ * DMA macros
++ *
++ ****************************************************************************/
++#define DMA_HANDLE_SPECIFIER_MASK 0xF0000000
++#define DMA_CH0_HANDLE_SPECIFIER 0x00000000
++#define DMA_CH1_HANDLE_SPECIFIER 0x10000000
++#define DMA_CH2_HANDLE_SPECIFIER 0x20000000
++#define DMA_CH3_HANDLE_SPECIFIER 0x30000000
++#define DMA_CH4_HANDLE_SPECIFIER 0x40000000
++#define DMA_CH5_HANDLE_SPECIFIER 0x50000000
++#define DMA_CH6_HANDLE_SPECIFIER 0x60000000
++#define DMA_CH7_HANDLE_SPECIFIER 0x70000000
++#define DMA_CH8_HANDLE_SPECIFIER 0x80000000
++#define DMA_CH9_HANDLE_SPECIFIER 0x90000000
++#define DMA_CH10_HANDLE_SPECIFIER 0xA0000000
++#define DMA_CH11_HANDLE_SPECIFIER 0xB0000000
++
++#endif // _DMADRV_H_