2005-04-17 02:20:36 +04:00
/*
* linux / arch / arm / kernel / dma . c
*
* Copyright ( C ) 1995 - 2000 Russell King
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*
* Front - end to the DMA handling . This handles the allocation / freeing
* of DMA channels , and provides a unified interface to the machines
* DMA facilities .
*/
# include <linux/module.h>
# include <linux/init.h>
# include <linux/spinlock.h>
# include <linux/errno.h>
2008-12-08 20:50:25 +03:00
# include <linux/scatterlist.h>
2005-04-17 02:20:36 +04:00
# include <asm/dma.h>
# include <asm/mach/dma.h>
DEFINE_SPINLOCK ( dma_spin_lock ) ;
2006-01-04 18:52:45 +03:00
EXPORT_SYMBOL ( dma_spin_lock ) ;
2005-04-17 02:20:36 +04:00
2008-12-08 19:33:30 +03:00
static dma_t * dma_chan [ MAX_DMA_CHANNELS ] ;
2005-04-17 02:20:36 +04:00
2008-12-08 19:08:48 +03:00
static inline dma_t * dma_channel ( unsigned int chan )
{
2008-12-08 19:33:30 +03:00
if ( chan > = MAX_DMA_CHANNELS )
2008-12-08 19:08:48 +03:00
return NULL ;
2008-12-08 19:33:30 +03:00
return dma_chan [ chan ] ;
}
int __init isa_dma_add ( unsigned int chan , dma_t * dma )
{
if ( ! dma - > d_ops )
return - EINVAL ;
2008-12-08 20:50:25 +03:00
sg_init_table ( & dma - > buf , 1 ) ;
2008-12-08 19:33:30 +03:00
if ( dma_chan [ chan ] )
return - EBUSY ;
dma_chan [ chan ] = dma ;
return 0 ;
2008-12-08 19:08:48 +03:00
}
2005-04-17 02:20:36 +04:00
/*
* Request DMA channel
*
* On certain platforms , we have to allocate an interrupt as well . . .
*/
2008-12-08 18:58:50 +03:00
int request_dma ( unsigned int chan , const char * device_id )
2005-04-17 02:20:36 +04:00
{
2008-12-08 19:08:48 +03:00
dma_t * dma = dma_channel ( chan ) ;
2005-04-17 02:20:36 +04:00
int ret ;
2008-12-08 19:08:48 +03:00
if ( ! dma )
2005-04-17 02:20:36 +04:00
goto bad_dma ;
if ( xchg ( & dma - > lock , 1 ) ! = 0 )
goto busy ;
dma - > device_id = device_id ;
dma - > active = 0 ;
dma - > invalid = 1 ;
ret = 0 ;
if ( dma - > d_ops - > request )
2008-12-08 18:58:50 +03:00
ret = dma - > d_ops - > request ( chan , dma ) ;
2005-04-17 02:20:36 +04:00
if ( ret )
xchg ( & dma - > lock , 0 ) ;
return ret ;
bad_dma :
2008-12-08 18:58:50 +03:00
printk ( KERN_ERR " dma: trying to allocate DMA%d \n " , chan ) ;
2005-04-17 02:20:36 +04:00
return - EINVAL ;
busy :
return - EBUSY ;
}
2006-01-04 18:52:45 +03:00
EXPORT_SYMBOL ( request_dma ) ;
2005-04-17 02:20:36 +04:00
/*
* Free DMA channel
*
* On certain platforms , we have to free interrupt as well . . .
*/
2008-12-08 18:58:50 +03:00
void free_dma ( unsigned int chan )
2005-04-17 02:20:36 +04:00
{
2008-12-08 19:08:48 +03:00
dma_t * dma = dma_channel ( chan ) ;
2005-04-17 02:20:36 +04:00
2008-12-08 19:08:48 +03:00
if ( ! dma )
2005-04-17 02:20:36 +04:00
goto bad_dma ;
if ( dma - > active ) {
2008-12-08 18:58:50 +03:00
printk ( KERN_ERR " dma%d: freeing active DMA \n " , chan ) ;
dma - > d_ops - > disable ( chan , dma ) ;
2005-04-17 02:20:36 +04:00
dma - > active = 0 ;
}
if ( xchg ( & dma - > lock , 0 ) ! = 0 ) {
if ( dma - > d_ops - > free )
2008-12-08 18:58:50 +03:00
dma - > d_ops - > free ( chan , dma ) ;
2005-04-17 02:20:36 +04:00
return ;
}
2008-12-08 18:58:50 +03:00
printk ( KERN_ERR " dma%d: trying to free free DMA \n " , chan ) ;
2005-04-17 02:20:36 +04:00
return ;
bad_dma :
2008-12-08 18:58:50 +03:00
printk ( KERN_ERR " dma: trying to free DMA%d \n " , chan ) ;
2005-04-17 02:20:36 +04:00
}
2006-01-04 18:52:45 +03:00
EXPORT_SYMBOL ( free_dma ) ;
2005-04-17 02:20:36 +04:00
/* Set DMA Scatter-Gather list
*/
2008-12-08 18:58:50 +03:00
void set_dma_sg ( unsigned int chan , struct scatterlist * sg , int nr_sg )
2005-04-17 02:20:36 +04:00
{
2008-12-08 19:08:48 +03:00
dma_t * dma = dma_channel ( chan ) ;
2005-04-17 02:20:36 +04:00
if ( dma - > active )
printk ( KERN_ERR " dma%d: altering DMA SG while "
2008-12-08 18:58:50 +03:00
" DMA active \n " , chan ) ;
2005-04-17 02:20:36 +04:00
dma - > sg = sg ;
dma - > sgcount = nr_sg ;
dma - > invalid = 1 ;
}
2006-01-04 18:52:45 +03:00
EXPORT_SYMBOL ( set_dma_sg ) ;
2005-04-17 02:20:36 +04:00
/* Set DMA address
*
* Copy address to the structure , and set the invalid bit
*/
2008-12-08 18:58:50 +03:00
void __set_dma_addr ( unsigned int chan , void * addr )
2005-04-17 02:20:36 +04:00
{
2008-12-08 19:08:48 +03:00
dma_t * dma = dma_channel ( chan ) ;
2005-04-17 02:20:36 +04:00
if ( dma - > active )
printk ( KERN_ERR " dma%d: altering DMA address while "
2008-12-08 18:58:50 +03:00
" DMA active \n " , chan ) ;
2005-04-17 02:20:36 +04:00
2006-01-04 18:08:30 +03:00
dma - > sg = NULL ;
dma - > addr = addr ;
2005-04-17 02:20:36 +04:00
dma - > invalid = 1 ;
}
2006-01-04 18:52:45 +03:00
EXPORT_SYMBOL ( __set_dma_addr ) ;
2005-04-17 02:20:36 +04:00
/* Set DMA byte count
*
* Copy address to the structure , and set the invalid bit
*/
2008-12-08 18:58:50 +03:00
void set_dma_count ( unsigned int chan , unsigned long count )
2005-04-17 02:20:36 +04:00
{
2008-12-08 19:08:48 +03:00
dma_t * dma = dma_channel ( chan ) ;
2005-04-17 02:20:36 +04:00
if ( dma - > active )
printk ( KERN_ERR " dma%d: altering DMA count while "
2008-12-08 18:58:50 +03:00
" DMA active \n " , chan ) ;
2005-04-17 02:20:36 +04:00
2006-01-04 18:08:30 +03:00
dma - > sg = NULL ;
dma - > count = count ;
2005-04-17 02:20:36 +04:00
dma - > invalid = 1 ;
}
2006-01-04 18:52:45 +03:00
EXPORT_SYMBOL ( set_dma_count ) ;
2005-04-17 02:20:36 +04:00
/* Set DMA direction mode
*/
2009-01-02 15:34:55 +03:00
void set_dma_mode ( unsigned int chan , unsigned int mode )
2005-04-17 02:20:36 +04:00
{
2008-12-08 19:08:48 +03:00
dma_t * dma = dma_channel ( chan ) ;
2005-04-17 02:20:36 +04:00
if ( dma - > active )
printk ( KERN_ERR " dma%d: altering DMA mode while "
2008-12-08 18:58:50 +03:00
" DMA active \n " , chan ) ;
2005-04-17 02:20:36 +04:00
dma - > dma_mode = mode ;
dma - > invalid = 1 ;
}
2006-01-04 18:52:45 +03:00
EXPORT_SYMBOL ( set_dma_mode ) ;
2005-04-17 02:20:36 +04:00
/* Enable DMA channel
*/
2008-12-08 18:58:50 +03:00
void enable_dma ( unsigned int chan )
2005-04-17 02:20:36 +04:00
{
2008-12-08 19:08:48 +03:00
dma_t * dma = dma_channel ( chan ) ;
2005-04-17 02:20:36 +04:00
if ( ! dma - > lock )
goto free_dma ;
if ( dma - > active = = 0 ) {
dma - > active = 1 ;
2008-12-08 18:58:50 +03:00
dma - > d_ops - > enable ( chan , dma ) ;
2005-04-17 02:20:36 +04:00
}
return ;
free_dma :
2008-12-08 18:58:50 +03:00
printk ( KERN_ERR " dma%d: trying to enable free DMA \n " , chan ) ;
2005-04-17 02:20:36 +04:00
BUG ( ) ;
}
2006-01-04 18:52:45 +03:00
EXPORT_SYMBOL ( enable_dma ) ;
2005-04-17 02:20:36 +04:00
/* Disable DMA channel
*/
2008-12-08 18:58:50 +03:00
void disable_dma ( unsigned int chan )
2005-04-17 02:20:36 +04:00
{
2008-12-08 19:08:48 +03:00
dma_t * dma = dma_channel ( chan ) ;
2005-04-17 02:20:36 +04:00
if ( ! dma - > lock )
goto free_dma ;
if ( dma - > active = = 1 ) {
dma - > active = 0 ;
2008-12-08 18:58:50 +03:00
dma - > d_ops - > disable ( chan , dma ) ;
2005-04-17 02:20:36 +04:00
}
return ;
free_dma :
2008-12-08 18:58:50 +03:00
printk ( KERN_ERR " dma%d: trying to disable free DMA \n " , chan ) ;
2005-04-17 02:20:36 +04:00
BUG ( ) ;
}
2006-01-04 18:52:45 +03:00
EXPORT_SYMBOL ( disable_dma ) ;
2005-04-17 02:20:36 +04:00
/*
* Is the specified DMA channel active ?
*/
2008-12-08 18:58:50 +03:00
int dma_channel_active ( unsigned int chan )
2005-04-17 02:20:36 +04:00
{
2008-12-08 19:08:48 +03:00
dma_t * dma = dma_channel ( chan ) ;
return dma - > active ;
2005-04-17 02:20:36 +04:00
}
2007-04-01 00:36:53 +04:00
EXPORT_SYMBOL ( dma_channel_active ) ;
2005-04-17 02:20:36 +04:00
2008-12-08 18:58:50 +03:00
void set_dma_page ( unsigned int chan , char pagenr )
2005-04-17 02:20:36 +04:00
{
2008-12-08 18:58:50 +03:00
printk ( KERN_ERR " dma%d: trying to set_dma_page \n " , chan ) ;
2005-04-17 02:20:36 +04:00
}
2006-01-04 18:52:45 +03:00
EXPORT_SYMBOL ( set_dma_page ) ;
2005-04-17 02:20:36 +04:00
2008-12-08 18:58:50 +03:00
void set_dma_speed ( unsigned int chan , int cycle_ns )
2005-04-17 02:20:36 +04:00
{
2008-12-08 19:08:48 +03:00
dma_t * dma = dma_channel ( chan ) ;
2005-04-17 02:20:36 +04:00
int ret = 0 ;
if ( dma - > d_ops - > setspeed )
2008-12-08 18:58:50 +03:00
ret = dma - > d_ops - > setspeed ( chan , dma , cycle_ns ) ;
2005-04-17 02:20:36 +04:00
dma - > speed = ret ;
}
2006-01-04 18:52:45 +03:00
EXPORT_SYMBOL ( set_dma_speed ) ;
2005-04-17 02:20:36 +04:00
2008-12-08 18:58:50 +03:00
int get_dma_residue ( unsigned int chan )
2005-04-17 02:20:36 +04:00
{
2008-12-08 19:08:48 +03:00
dma_t * dma = dma_channel ( chan ) ;
2005-04-17 02:20:36 +04:00
int ret = 0 ;
if ( dma - > d_ops - > residue )
2008-12-08 18:58:50 +03:00
ret = dma - > d_ops - > residue ( chan , dma ) ;
2005-04-17 02:20:36 +04:00
return ret ;
}
2006-01-04 18:52:45 +03:00
EXPORT_SYMBOL ( get_dma_residue ) ;