2005-04-17 02:20:36 +04:00
/*
* linux / arch / arm / mach - imx / dma . c
*
* imx DMA registration and IRQ dispatching
*
* 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 .
*
2006-04-02 19:58:37 +04:00
* 2004 - 03 - 03 Sascha Hauer < sascha @ saschahauer . de >
2005-04-17 02:20:36 +04:00
* initial version heavily inspired by
* linux / arch / arm / mach - pxa / dma . c
2006-04-02 19:58:37 +04:00
*
* 2005 - 04 - 17 Pavel Pisa < pisa @ cmp . felk . cvut . cz >
* Changed to support scatter gather DMA
* by taking Russell ' s code from RiscPC
*
2006-06-23 01:21:03 +04:00
* 2006 - 05 - 31 Pavel Pisa < pisa @ cmp . felk . cvut . cz >
* Corrected error handling code .
*
2005-04-17 02:20:36 +04:00
*/
2006-04-02 19:58:37 +04:00
# undef DEBUG
2005-04-17 02:20:36 +04:00
# include <linux/module.h>
# include <linux/init.h>
# include <linux/kernel.h>
# include <linux/interrupt.h>
# include <linux/errno.h>
2008-11-29 14:40:28 +03:00
# include <asm/scatterlist.h>
2005-04-17 02:20:36 +04:00
# include <asm/system.h>
# include <asm/irq.h>
2008-08-05 19:14:15 +04:00
# include <mach/hardware.h>
2008-11-29 14:40:28 +03:00
# include <mach/dma.h>
2008-08-05 19:14:15 +04:00
# include <mach/imx-dma.h>
2006-04-02 19:58:37 +04:00
struct imx_dma_channel imx_dma_channels [ IMX_DMA_CHANNELS ] ;
/*
* imx_dma_sg_next - prepare next chunk for scatter - gather DMA emulation
* @ dma_ch : i . MX DMA channel number
* @ lastcount : number of bytes transferred during last transfer
*
* Functions prepares DMA controller for next sg data chunk transfer .
* The @ lastcount argument informs function about number of bytes transferred
* during last block . Zero value can be used for @ lastcount to setup DMA
* for the first chunk .
*/
static inline int imx_dma_sg_next ( imx_dmach_t dma_ch , unsigned int lastcount )
{
struct imx_dma_channel * imxdma = & imx_dma_channels [ dma_ch ] ;
unsigned int nextcount ;
unsigned int nextaddr ;
if ( ! imxdma - > name ) {
printk ( KERN_CRIT " %s: called for not allocated channel %d \n " ,
2008-03-05 02:08:02 +03:00
__func__ , dma_ch ) ;
2006-04-02 19:58:37 +04:00
return 0 ;
}
imxdma - > resbytes - = lastcount ;
if ( ! imxdma - > sg ) {
pr_debug ( " imxdma%d: no sg data \n " , dma_ch ) ;
return 0 ;
}
imxdma - > sgbc + = lastcount ;
if ( ( imxdma - > sgbc > = imxdma - > sg - > length ) | | ! imxdma - > resbytes ) {
if ( ( imxdma - > sgcount < = 1 ) | | ! imxdma - > resbytes ) {
pr_debug ( " imxdma%d: sg transfer limit reached \n " ,
dma_ch ) ;
imxdma - > sgcount = 0 ;
imxdma - > sg = NULL ;
return 0 ;
} else {
imxdma - > sgcount - - ;
imxdma - > sg + + ;
imxdma - > sgbc = 0 ;
}
}
nextcount = imxdma - > sg - > length - imxdma - > sgbc ;
nextaddr = imxdma - > sg - > dma_address + imxdma - > sgbc ;
2005-04-17 02:20:36 +04:00
2006-04-02 19:58:37 +04:00
if ( imxdma - > resbytes < nextcount )
nextcount = imxdma - > resbytes ;
2005-04-17 02:20:36 +04:00
2006-04-02 19:58:37 +04:00
if ( ( imxdma - > dma_mode & DMA_MODE_MASK ) = = DMA_MODE_READ )
DAR ( dma_ch ) = nextaddr ;
else
SAR ( dma_ch ) = nextaddr ;
CNTR ( dma_ch ) = nextcount ;
pr_debug ( " imxdma%d: next sg chunk dst 0x%08x, src 0x%08x, size 0x%08x \n " ,
dma_ch , DAR ( dma_ch ) , SAR ( dma_ch ) , CNTR ( dma_ch ) ) ;
return nextcount ;
}
/*
* imx_dma_setup_sg_base - scatter - gather DMA emulation
* @ dma_ch : i . MX DMA channel number
* @ sg : pointer to the scatter - gather list / vector
* @ sgcount : scatter - gather list hungs count
*
* Functions sets up i . MX DMA state for emulated scatter - gather transfer
* and sets up channel registers to be ready for the first chunk
*/
static int
imx_dma_setup_sg_base ( imx_dmach_t dma_ch ,
struct scatterlist * sg , unsigned int sgcount )
{
struct imx_dma_channel * imxdma = & imx_dma_channels [ dma_ch ] ;
imxdma - > sg = sg ;
imxdma - > sgcount = sgcount ;
imxdma - > sgbc = 0 ;
return imx_dma_sg_next ( dma_ch , 0 ) ;
}
/**
* imx_dma_setup_single - setup i . MX DMA channel for linear memory to / from device transfer
* @ dma_ch : i . MX DMA channel number
* @ dma_address : the DMA / physical memory address of the linear data block
* to transfer
* @ dma_length : length of the data block in bytes
* @ dev_addr : physical device port address
* @ dmamode : DMA transfer mode , % DMA_MODE_READ from the device to the memory
* or % DMA_MODE_WRITE from memory to the device
*
* The function setups DMA channel source and destination addresses for transfer
* specified by provided parameters . The scatter - gather emulation is disabled ,
* because linear data block
2007-05-11 23:40:30 +04:00
* form the physical address range is transferred .
2006-04-02 19:58:37 +04:00
* Return value : if incorrect parameters are provided - % EINVAL .
* Zero indicates success .
*/
2005-04-17 02:20:36 +04:00
int
2006-04-02 19:58:37 +04:00
imx_dma_setup_single ( imx_dmach_t dma_ch , dma_addr_t dma_address ,
unsigned int dma_length , unsigned int dev_addr ,
2008-11-29 14:40:28 +03:00
unsigned int dmamode )
2005-04-17 02:20:36 +04:00
{
2006-04-02 19:58:37 +04:00
struct imx_dma_channel * imxdma = & imx_dma_channels [ dma_ch ] ;
2005-04-17 02:20:36 +04:00
2006-04-02 19:58:37 +04:00
imxdma - > sg = NULL ;
imxdma - > sgcount = 0 ;
imxdma - > dma_mode = dmamode ;
imxdma - > resbytes = dma_length ;
if ( ! dma_address ) {
printk ( KERN_ERR " imxdma%d: imx_dma_setup_single null address \n " ,
dma_ch ) ;
2005-04-17 02:20:36 +04:00
return - EINVAL ;
2006-04-02 19:58:37 +04:00
}
2005-04-17 02:20:36 +04:00
2006-04-02 19:58:37 +04:00
if ( ! dma_length ) {
printk ( KERN_ERR " imxdma%d: imx_dma_setup_single zero length \n " ,
dma_ch ) ;
return - EINVAL ;
}
2005-04-17 02:20:36 +04:00
2006-04-02 19:58:37 +04:00
if ( ( dmamode & DMA_MODE_MASK ) = = DMA_MODE_READ ) {
pr_debug ( " imxdma%d: mx_dma_setup_single2dev dma_addressg=0x%08x dma_length=%d dev_addr=0x%08x for read \n " ,
dma_ch , ( unsigned int ) dma_address , dma_length ,
dev_addr ) ;
SAR ( dma_ch ) = dev_addr ;
DAR ( dma_ch ) = ( unsigned int ) dma_address ;
} else if ( ( dmamode & DMA_MODE_MASK ) = = DMA_MODE_WRITE ) {
pr_debug ( " imxdma%d: mx_dma_setup_single2dev dma_addressg=0x%08x dma_length=%d dev_addr=0x%08x for write \n " ,
dma_ch , ( unsigned int ) dma_address , dma_length ,
dev_addr ) ;
SAR ( dma_ch ) = ( unsigned int ) dma_address ;
DAR ( dma_ch ) = dev_addr ;
} else {
printk ( KERN_ERR " imxdma%d: imx_dma_setup_single bad dmamode \n " ,
dma_ch ) ;
return - EINVAL ;
2005-04-17 02:20:36 +04:00
}
2006-04-02 19:58:37 +04:00
CNTR ( dma_ch ) = dma_length ;
return 0 ;
}
/**
* imx_dma_setup_sg - setup i . MX DMA channel SG list to / from device transfer
* @ dma_ch : i . MX DMA channel number
* @ sg : pointer to the scatter - gather list / vector
* @ sgcount : scatter - gather list hungs count
* @ dma_length : total length of the transfer request in bytes
* @ dev_addr : physical device port address
* @ dmamode : DMA transfer mode , % DMA_MODE_READ from the device to the memory
* or % DMA_MODE_WRITE from memory to the device
*
2007-05-11 23:40:30 +04:00
* The function sets up DMA channel state and registers to be ready for transfer
2006-04-02 19:58:37 +04:00
* specified by provided parameters . The scatter - gather emulation is set up
* according to the parameters .
*
* The full preparation of the transfer requires setup of more register
* by the caller before imx_dma_enable ( ) can be called .
*
* % BLR ( dma_ch ) holds transfer burst length in bytes , 0 means 64 bytes
*
* % RSSR ( dma_ch ) has to be set to the DMA request line source % DMA_REQ_xxx
*
* % CCR ( dma_ch ) has to specify transfer parameters , the next settings is typical
* for linear or simple scatter - gather transfers if % DMA_MODE_READ is specified
*
* % CCR_DMOD_LINEAR | % CCR_DSIZ_32 | % CCR_SMOD_FIFO | % CCR_SSIZ_x
*
* The typical setup for % DMA_MODE_WRITE is specified by next options combination
*
* % CCR_SMOD_LINEAR | % CCR_SSIZ_32 | % CCR_DMOD_FIFO | % CCR_DSIZ_x
*
2007-05-11 23:40:30 +04:00
* Be careful here and do not mistakenly mix source and target device
2006-04-02 19:58:37 +04:00
* port sizes constants , they are really different :
* % CCR_SSIZ_8 , % CCR_SSIZ_16 , % CCR_SSIZ_32 ,
* % CCR_DSIZ_8 , % CCR_DSIZ_16 , % CCR_DSIZ_32
*
* Return value : if incorrect parameters are provided - % EINVAL .
* Zero indicates success .
*/
int
imx_dma_setup_sg ( imx_dmach_t dma_ch ,
struct scatterlist * sg , unsigned int sgcount , unsigned int dma_length ,
2008-11-29 14:40:28 +03:00
unsigned int dev_addr , unsigned int dmamode )
2006-04-02 19:58:37 +04:00
{
int res ;
struct imx_dma_channel * imxdma = & imx_dma_channels [ dma_ch ] ;
imxdma - > sg = NULL ;
imxdma - > sgcount = 0 ;
imxdma - > dma_mode = dmamode ;
imxdma - > resbytes = dma_length ;
if ( ! sg | | ! sgcount ) {
printk ( KERN_ERR " imxdma%d: imx_dma_setup_sg epty sg list \n " ,
dma_ch ) ;
return - EINVAL ;
}
if ( ! sg - > length ) {
printk ( KERN_ERR " imxdma%d: imx_dma_setup_sg zero length \n " ,
dma_ch ) ;
return - EINVAL ;
2005-04-17 02:20:36 +04:00
}
2006-04-02 19:58:37 +04:00
if ( ( dmamode & DMA_MODE_MASK ) = = DMA_MODE_READ ) {
pr_debug ( " imxdma%d: mx_dma_setup_sg2dev sg=%p sgcount=%d total length=%d dev_addr=0x%08x for read \n " ,
dma_ch , sg , sgcount , dma_length , dev_addr ) ;
SAR ( dma_ch ) = dev_addr ;
} else if ( ( dmamode & DMA_MODE_MASK ) = = DMA_MODE_WRITE ) {
pr_debug ( " imxdma%d: mx_dma_setup_sg2dev sg=%p sgcount=%d total length=%d dev_addr=0x%08x for write \n " ,
dma_ch , sg , sgcount , dma_length , dev_addr ) ;
DAR ( dma_ch ) = dev_addr ;
2005-04-17 02:20:36 +04:00
} else {
2006-04-02 19:58:37 +04:00
printk ( KERN_ERR " imxdma%d: imx_dma_setup_sg bad dmamode \n " ,
dma_ch ) ;
return - EINVAL ;
}
res = imx_dma_setup_sg_base ( dma_ch , sg , sgcount ) ;
if ( res < = 0 ) {
printk ( KERN_ERR " imxdma%d: no sg chunk ready \n " , dma_ch ) ;
return - EINVAL ;
}
return 0 ;
}
/**
* imx_dma_setup_handlers - setup i . MX DMA channel end and error notification handlers
* @ dma_ch : i . MX DMA channel number
* @ irq_handler : the pointer to the function called if the transfer
* ends successfully
* @ err_handler : the pointer to the function called if the premature
* end caused by error occurs
* @ data : user specified value to be passed to the handlers
*/
int
imx_dma_setup_handlers ( imx_dmach_t dma_ch ,
2006-10-06 21:53:39 +04:00
void ( * irq_handler ) ( int , void * ) ,
void ( * err_handler ) ( int , void * , int ) ,
2006-04-02 19:58:37 +04:00
void * data )
{
struct imx_dma_channel * imxdma = & imx_dma_channels [ dma_ch ] ;
unsigned long flags ;
if ( ! imxdma - > name ) {
printk ( KERN_CRIT " %s: called for not allocated channel %d \n " ,
2008-03-05 02:08:02 +03:00
__func__ , dma_ch ) ;
2006-04-02 19:58:37 +04:00
return - ENODEV ;
}
local_irq_save ( flags ) ;
DISR = ( 1 < < dma_ch ) ;
imxdma - > irq_handler = irq_handler ;
imxdma - > err_handler = err_handler ;
imxdma - > data = data ;
local_irq_restore ( flags ) ;
return 0 ;
}
/**
* imx_dma_enable - function to start i . MX DMA channel operation
* @ dma_ch : i . MX DMA channel number
*
* The channel has to be allocated by driver through imx_dma_request ( )
* or imx_dma_request_by_prio ( ) function .
* The transfer parameters has to be set to the channel registers through
* call of the imx_dma_setup_single ( ) or imx_dma_setup_sg ( ) function
* and registers % BLR ( dma_ch ) , % RSSR ( dma_ch ) and % CCR ( dma_ch ) has to
* be set prior this function call by the channel user .
*/
void imx_dma_enable ( imx_dmach_t dma_ch )
{
struct imx_dma_channel * imxdma = & imx_dma_channels [ dma_ch ] ;
unsigned long flags ;
pr_debug ( " imxdma%d: imx_dma_enable \n " , dma_ch ) ;
if ( ! imxdma - > name ) {
printk ( KERN_CRIT " %s: called for not allocated channel %d \n " ,
2008-03-05 02:08:02 +03:00
__func__ , dma_ch ) ;
2006-04-02 19:58:37 +04:00
return ;
}
local_irq_save ( flags ) ;
DISR = ( 1 < < dma_ch ) ;
DIMR & = ~ ( 1 < < dma_ch ) ;
CCR ( dma_ch ) | = CCR_CEN ;
local_irq_restore ( flags ) ;
}
/**
* imx_dma_disable - stop , finish i . MX DMA channel operatin
* @ dma_ch : i . MX DMA channel number
*/
void imx_dma_disable ( imx_dmach_t dma_ch )
{
unsigned long flags ;
pr_debug ( " imxdma%d: imx_dma_disable \n " , dma_ch ) ;
local_irq_save ( flags ) ;
DIMR | = ( 1 < < dma_ch ) ;
CCR ( dma_ch ) & = ~ CCR_CEN ;
DISR = ( 1 < < dma_ch ) ;
local_irq_restore ( flags ) ;
}
/**
* imx_dma_request - request / allocate specified channel number
* @ dma_ch : i . MX DMA channel number
* @ name : the driver / caller own non - % NULL identification
*/
int imx_dma_request ( imx_dmach_t dma_ch , const char * name )
{
struct imx_dma_channel * imxdma = & imx_dma_channels [ dma_ch ] ;
unsigned long flags ;
/* basic sanity checks */
if ( ! name )
return - EINVAL ;
if ( dma_ch > = IMX_DMA_CHANNELS ) {
printk ( KERN_CRIT " %s: called for non-existed channel %d \n " ,
2008-03-05 02:08:02 +03:00
__func__ , dma_ch ) ;
2006-04-02 19:58:37 +04:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
}
2006-04-02 19:58:37 +04:00
local_irq_save ( flags ) ;
if ( imxdma - > name ) {
local_irq_restore ( flags ) ;
return - ENODEV ;
}
imxdma - > name = name ;
imxdma - > irq_handler = NULL ;
imxdma - > err_handler = NULL ;
imxdma - > data = NULL ;
imxdma - > sg = NULL ;
2005-04-17 02:20:36 +04:00
local_irq_restore ( flags ) ;
2006-04-02 19:58:37 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2006-04-02 19:58:37 +04:00
/**
* imx_dma_free - release previously acquired channel
* @ dma_ch : i . MX DMA channel number
*/
void imx_dma_free ( imx_dmach_t dma_ch )
2005-04-17 02:20:36 +04:00
{
unsigned long flags ;
2006-04-02 19:58:37 +04:00
struct imx_dma_channel * imxdma = & imx_dma_channels [ dma_ch ] ;
2005-04-17 02:20:36 +04:00
2006-04-02 19:58:37 +04:00
if ( ! imxdma - > name ) {
2005-04-17 02:20:36 +04:00
printk ( KERN_CRIT
" %s: trying to free channel %d which is already freed \n " ,
2008-03-05 02:08:02 +03:00
__func__ , dma_ch ) ;
2005-04-17 02:20:36 +04:00
return ;
}
local_irq_save ( flags ) ;
2006-04-02 19:58:37 +04:00
/* Disable interrupts */
DIMR | = ( 1 < < dma_ch ) ;
CCR ( dma_ch ) & = ~ CCR_CEN ;
imxdma - > name = NULL ;
2005-04-17 02:20:36 +04:00
local_irq_restore ( flags ) ;
}
2006-04-02 19:58:37 +04:00
/**
* imx_dma_request_by_prio - find and request some of free channels best suiting requested priority
* @ name : the driver / caller own non - % NULL identification
* @ prio : one of the hardware distinguished priority level :
* % DMA_PRIO_HIGH , % DMA_PRIO_MEDIUM , % DMA_PRIO_LOW
*
* This function tries to find free channel in the specified priority group
* if the priority cannot be achieved it tries to look for free channel
* in the higher and then even lower priority groups .
*
* Return value : If there is no free channel to allocate , - % ENODEV is returned .
2008-06-25 16:25:13 +04:00
* On successful allocation channel is returned .
2006-04-02 19:58:37 +04:00
*/
2008-06-25 16:25:13 +04:00
imx_dmach_t imx_dma_request_by_prio ( const char * name , imx_dma_prio prio )
2006-04-02 19:58:37 +04:00
{
int i ;
int best ;
switch ( prio ) {
case ( DMA_PRIO_HIGH ) :
best = 8 ;
break ;
case ( DMA_PRIO_MEDIUM ) :
best = 4 ;
break ;
case ( DMA_PRIO_LOW ) :
default :
best = 0 ;
break ;
}
for ( i = best ; i < IMX_DMA_CHANNELS ; i + + ) {
if ( ! imx_dma_request ( i , name ) ) {
2008-06-25 16:25:13 +04:00
return i ;
2006-04-02 19:58:37 +04:00
}
}
for ( i = best - 1 ; i > = 0 ; i - - ) {
if ( ! imx_dma_request ( i , name ) ) {
2008-06-25 16:25:13 +04:00
return i ;
2006-04-02 19:58:37 +04:00
}
}
2008-03-05 02:08:02 +03:00
printk ( KERN_ERR " %s: no free DMA channel found \n " , __func__ ) ;
2006-04-02 19:58:37 +04:00
return - ENODEV ;
}
2006-10-06 21:53:39 +04:00
static irqreturn_t dma_err_handler ( int irq , void * dev_id )
2005-04-17 02:20:36 +04:00
{
int i , disr = DISR ;
2006-04-02 19:58:37 +04:00
struct imx_dma_channel * channel ;
2005-04-17 02:20:36 +04:00
unsigned int err_mask = DBTOSR | DRTOSR | DSESR | DBOSR ;
2006-06-23 01:21:03 +04:00
int errcode ;
2005-04-17 02:20:36 +04:00
2006-06-23 01:21:03 +04:00
DISR = disr & err_mask ;
2006-04-02 19:58:37 +04:00
for ( i = 0 ; i < IMX_DMA_CHANNELS ; i + + ) {
2006-06-23 01:21:03 +04:00
if ( ! ( err_mask & ( 1 < < i ) ) )
2005-04-17 02:20:36 +04:00
continue ;
2006-06-23 01:21:03 +04:00
channel = & imx_dma_channels [ i ] ;
errcode = 0 ;
2006-04-02 19:58:37 +04:00
2005-04-17 02:20:36 +04:00
if ( DBTOSR & ( 1 < < i ) ) {
2006-06-23 01:21:03 +04:00
DBTOSR = ( 1 < < i ) ;
errcode | = IMX_DMA_ERR_BURST ;
2005-04-17 02:20:36 +04:00
}
if ( DRTOSR & ( 1 < < i ) ) {
2006-06-23 01:21:03 +04:00
DRTOSR = ( 1 < < i ) ;
errcode | = IMX_DMA_ERR_REQUEST ;
2005-04-17 02:20:36 +04:00
}
if ( DSESR & ( 1 < < i ) ) {
2006-06-23 01:21:03 +04:00
DSESR = ( 1 < < i ) ;
errcode | = IMX_DMA_ERR_TRANSFER ;
2005-04-17 02:20:36 +04:00
}
if ( DBOSR & ( 1 < < i ) ) {
2006-06-23 01:21:03 +04:00
DBOSR = ( 1 < < i ) ;
errcode | = IMX_DMA_ERR_BUFFER ;
2005-04-17 02:20:36 +04:00
}
2006-06-23 01:21:03 +04:00
/*
* The cleaning of @ sg field would be questionable
* there , because its value can help to compute
2007-05-11 23:40:30 +04:00
* remaining / transferred bytes count in the handler
2006-06-23 01:21:03 +04:00
*/
/*imx_dma_channels[i].sg = NULL;*/
if ( channel - > name & & channel - > err_handler ) {
2006-10-06 21:53:39 +04:00
channel - > err_handler ( i , channel - > data , errcode ) ;
2006-06-23 01:21:03 +04:00
continue ;
}
imx_dma_channels [ i ] . sg = NULL ;
printk ( KERN_WARNING
" DMA timeout on channel %d (%s) -%s%s%s%s \n " ,
i , channel - > name ,
errcode & IMX_DMA_ERR_BURST ? " burst " : " " ,
errcode & IMX_DMA_ERR_REQUEST ? " request " : " " ,
errcode & IMX_DMA_ERR_TRANSFER ? " transfer " : " " ,
errcode & IMX_DMA_ERR_BUFFER ? " buffer " : " " ) ;
2005-04-17 02:20:36 +04:00
}
return IRQ_HANDLED ;
}
2006-10-06 21:53:39 +04:00
static irqreturn_t dma_irq_handler ( int irq , void * dev_id )
2005-04-17 02:20:36 +04:00
{
int i , disr = DISR ;
2006-04-02 19:58:37 +04:00
pr_debug ( " imxdma: dma_irq_handler called, disr=0x%08x \n " ,
disr ) ;
2005-04-17 02:20:36 +04:00
DISR = disr ;
2006-04-02 19:58:37 +04:00
for ( i = 0 ; i < IMX_DMA_CHANNELS ; i + + ) {
2005-04-17 02:20:36 +04:00
if ( disr & ( 1 < < i ) ) {
2006-04-02 19:58:37 +04:00
struct imx_dma_channel * channel = & imx_dma_channels [ i ] ;
if ( channel - > name ) {
if ( imx_dma_sg_next ( i , CNTR ( i ) ) ) {
CCR ( i ) & = ~ CCR_CEN ;
mb ( ) ;
CCR ( i ) | = CCR_CEN ;
} else {
if ( channel - > irq_handler )
channel - > irq_handler ( i ,
2006-10-06 21:53:39 +04:00
channel - > data ) ;
2006-04-02 19:58:37 +04:00
}
2005-04-17 02:20:36 +04:00
} else {
/*
* IRQ for an unregistered DMA channel :
* let ' s clear the interrupts and disable it .
*/
printk ( KERN_WARNING
" spurious IRQ for DMA channel %d \n " , i ) ;
}
}
}
return IRQ_HANDLED ;
}
2006-04-02 19:58:37 +04:00
static int __init imx_dma_init ( void )
2005-04-17 02:20:36 +04:00
{
int ret ;
2006-04-02 19:58:37 +04:00
int i ;
2005-04-17 02:20:36 +04:00
/* reset DMA module */
DCR = DCR_DRST ;
ret = request_irq ( DMA_INT , dma_irq_handler , 0 , " DMA " , NULL ) ;
if ( ret ) {
printk ( KERN_CRIT " Wow! Can't register IRQ for DMA \n " ) ;
return ret ;
}
ret = request_irq ( DMA_ERR , dma_err_handler , 0 , " DMA " , NULL ) ;
if ( ret ) {
printk ( KERN_CRIT " Wow! Can't register ERRIRQ for DMA \n " ) ;
free_irq ( DMA_INT , NULL ) ;
}
/* enable DMA module */
DCR = DCR_DEN ;
/* clear all interrupts */
2006-04-02 19:58:37 +04:00
DISR = ( 1 < < IMX_DMA_CHANNELS ) - 1 ;
2005-04-17 02:20:36 +04:00
/* enable interrupts */
2006-04-02 19:58:37 +04:00
DIMR = ( 1 < < IMX_DMA_CHANNELS ) - 1 ;
for ( i = 0 ; i < IMX_DMA_CHANNELS ; i + + ) {
imx_dma_channels [ i ] . sg = NULL ;
imx_dma_channels [ i ] . dma_num = i ;
}
2005-04-17 02:20:36 +04:00
return ret ;
}
arch_initcall ( imx_dma_init ) ;
2006-04-02 19:58:37 +04:00
EXPORT_SYMBOL ( imx_dma_setup_single ) ;
EXPORT_SYMBOL ( imx_dma_setup_sg ) ;
EXPORT_SYMBOL ( imx_dma_setup_handlers ) ;
EXPORT_SYMBOL ( imx_dma_enable ) ;
EXPORT_SYMBOL ( imx_dma_disable ) ;
EXPORT_SYMBOL ( imx_dma_request ) ;
EXPORT_SYMBOL ( imx_dma_free ) ;
EXPORT_SYMBOL ( imx_dma_request_by_prio ) ;
EXPORT_SYMBOL ( imx_dma_channels ) ;