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