⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 dma_ep93xx.c

📁 一个2.4.21版本的嵌入式linux内核
💻 C
📖 第 1 页 / 共 5 页
字号:
/****************************************************************************** * arch/arm/mach-ep9312/dma_ep93xx.c * * Support functions for the ep93xx internal DMA channels. * (see also Documentation/arm/ep93xx/dma.txt) * * Copyright (C) 2003  Cirrus Logic * * A large portion of this file is based on the dma api implemented by * Nicolas Pitre, dma-sa1100.c, copyrighted 2000. * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA * ****************************************************************************/#include <linux/module.h>#include <linux/init.h>#include <linux/sched.h>#include <linux/spinlock.h>#include <linux/slab.h>#include <linux/errno.h>#include <linux/delay.h>#include <asm/system.h>#include <asm/irq.h>#include <asm/hardware.h>#include <asm/io.h>#include <asm/dma.h>#include <asm/mach/dma.h>#include "dma_ep93xx.h"/***************************************************************************** * * Debugging macros * ****************************************************************************/#undef DEBUG//#define DEBUG   1#ifdef DEBUG#define DPRINTK( fmt, arg... )  printk( fmt, ##arg )#else#define DPRINTK( fmt, arg... )#endif/***************************************************************************** * * static global variables * ****************************************************************************/ep93xx_dma_t dma_chan[MAX_EP93XX_DMA_CHANNELS];/* *  lock used to protect the list of dma channels while searching for a free *  channel during dma_request. */static spinlock_t dma_list_lock;/***************************************************************************** * *  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 intdma_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 voiddma_m2m_irq_handler(int irq, void *dev_id, struct pt_regs *regs){	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.	 */

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -