--- /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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#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<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 +#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_