You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
openwrt/target/linux/ep93xx/patches-2.6.30/005-ep93xx-dma.patch

3623 lines
110 KiB
Diff

--- /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_