2008-10-16 09:01:26 +04:00
/*
2005-04-17 02:20:36 +04:00
* linux / kernel / dma . c : A DMA channel allocator . Inspired by linux / kernel / irq . c .
*
* Written by Hennus Bergman , 1992.
*
* 1994 / 12 / 26 : Changes by Alex Nash to fix a minor bug in / proc / dma .
* In the previous version the reported device could end up being wrong ,
* if a device requested a DMA channel that was already in use .
* [ It also happened to remove the sizeof ( char * ) = = sizeof ( int )
* assumption introduced because of those / proc / dma patches . - - Hennus ]
*/
2011-05-23 22:51:41 +04:00
# include <linux/export.h>
2005-04-17 02:20:36 +04:00
# include <linux/kernel.h>
# include <linux/errno.h>
# include <linux/spinlock.h>
# include <linux/string.h>
# include <linux/seq_file.h>
# include <linux/proc_fs.h>
# include <linux/init.h>
# include <asm/dma.h>
# include <asm/system.h>
2007-10-18 14:06:06 +04:00
2005-04-17 02:20:36 +04:00
/* A note on resource allocation:
*
* All drivers needing DMA channels , should allocate and release them
* through the public routines ` request_dma ( ) ' and ` free_dma ( ) ' .
*
* In order to avoid problems , all processes should allocate resources in
* the same sequence and release them in the reverse order .
*
* So , when allocating DMAs and IRQs , first allocate the IRQ , then the DMA .
* When releasing them , first release the DMA , then release the IRQ .
* If you don ' t , you may cause allocation requests to fail unnecessarily .
* This doesn ' t really matter now , but it will once we get real semaphores
* in the kernel .
*/
DEFINE_SPINLOCK ( dma_spin_lock ) ;
/*
* If our port doesn ' t define this it has no PC like DMA
*/
# ifdef MAX_DMA_CHANNELS
/* Channel n is busy iff dma_chan_busy[n].lock != 0.
* DMA0 used to be reserved for DRAM refresh , but apparently not any more . . .
* DMA4 is reserved for cascading .
*/
struct dma_chan {
int lock ;
const char * device_id ;
} ;
static struct dma_chan dma_chan_busy [ MAX_DMA_CHANNELS ] = {
[ 4 ] = { 1 , " cascade " } ,
} ;
2006-10-03 12:13:50 +04:00
/**
* request_dma - request and reserve a system DMA channel
* @ dmanr : DMA channel number
* @ device_id : reserving device ID string , used in / proc / dma
*/
2005-04-17 02:20:36 +04:00
int request_dma ( unsigned int dmanr , const char * device_id )
{
if ( dmanr > = MAX_DMA_CHANNELS )
return - EINVAL ;
if ( xchg ( & dma_chan_busy [ dmanr ] . lock , 1 ) ! = 0 )
return - EBUSY ;
dma_chan_busy [ dmanr ] . device_id = device_id ;
/* old flag was 0, now contains 1 to indicate busy */
return 0 ;
} /* request_dma */
2006-10-03 12:13:50 +04:00
/**
* free_dma - free a reserved system DMA channel
* @ dmanr : DMA channel number
*/
2005-04-17 02:20:36 +04:00
void free_dma ( unsigned int dmanr )
{
if ( dmanr > = MAX_DMA_CHANNELS ) {
printk ( KERN_WARNING " Trying to free DMA%d \n " , dmanr ) ;
return ;
}
if ( xchg ( & dma_chan_busy [ dmanr ] . lock , 0 ) = = 0 ) {
printk ( KERN_WARNING " Trying to free free DMA%d \n " , dmanr ) ;
return ;
2007-10-18 14:06:06 +04:00
}
2005-04-17 02:20:36 +04:00
} /* free_dma */
# else
int request_dma ( unsigned int dmanr , const char * device_id )
{
return - EINVAL ;
}
void free_dma ( unsigned int dmanr )
{
}
# endif
# ifdef CONFIG_PROC_FS
# ifdef MAX_DMA_CHANNELS
static int proc_dma_show ( struct seq_file * m , void * v )
{
int i ;
for ( i = 0 ; i < MAX_DMA_CHANNELS ; i + + ) {
if ( dma_chan_busy [ i ] . lock ) {
2007-10-18 14:06:06 +04:00
seq_printf ( m , " %2d: %s \n " , i ,
dma_chan_busy [ i ] . device_id ) ;
2005-04-17 02:20:36 +04:00
}
}
return 0 ;
}
# else
static int proc_dma_show ( struct seq_file * m , void * v )
{
seq_puts ( m , " No DMA \n " ) ;
return 0 ;
}
# endif /* MAX_DMA_CHANNELS */
static int proc_dma_open ( struct inode * inode , struct file * file )
{
return single_open ( file , proc_dma_show , NULL ) ;
}
2006-12-07 07:40:36 +03:00
static const struct file_operations proc_dma_operations = {
2005-04-17 02:20:36 +04:00
. open = proc_dma_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = single_release ,
} ;
static int __init proc_dma_init ( void )
{
2008-04-29 12:02:31 +04:00
proc_create ( " dma " , 0 , NULL , & proc_dma_operations ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
__initcall ( proc_dma_init ) ;
# endif
EXPORT_SYMBOL ( request_dma ) ;
EXPORT_SYMBOL ( free_dma ) ;
EXPORT_SYMBOL ( dma_spin_lock ) ;