2013-01-31 16:54:38 -08:00
/*
2009-11-13 22:54:13 +00:00
* Copyright 2003 - 2006 Simtec Electronics
2007-02-11 18:31:01 +01:00
* Ben Dooks < ben @ simtec . co . uk >
*
* S3C2410 DMA core
*
* http : //armlinux.simtec.co.uk/
*
* 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 .
*/
# ifdef CONFIG_S3C2410_DMA_DEBUG
# define DEBUG
# endif
# include <linux/module.h>
# include <linux/init.h>
# include <linux/sched.h>
# include <linux/spinlock.h>
# include <linux/interrupt.h>
2011-04-22 22:03:21 +02:00
# include <linux/syscore_ops.h>
2007-02-11 18:31:01 +01:00
# include <linux/slab.h>
# include <linux/errno.h>
2008-09-06 12:10:45 +01:00
# include <linux/io.h>
2007-02-11 18:31:01 +01:00
# include <asm/irq.h>
2008-08-05 16:14:15 +01:00
# include <mach/hardware.h>
2008-11-29 11:40:28 +00:00
# include <mach/dma.h>
2008-08-05 16:14:15 +01:00
# include <mach/map.h>
2007-02-11 18:31:01 +01:00
2010-02-20 23:01:33 +00:00
# include <plat/dma-s3c24xx.h>
2009-03-19 15:02:35 +00:00
# include <plat/regs-dma.h>
2007-02-11 18:31:01 +01:00
/* io map for dma */
static void __iomem * dma_base ;
static struct kmem_cache * dma_kmem ;
2007-02-17 15:37:14 +01:00
static int dma_channels ;
2007-04-20 11:39:46 +01:00
static struct s3c24xx_dma_selection dma_sel ;
2007-02-11 18:31:01 +01:00
/* debugging functions */
# define BUF_MAGIC (0xcafebabe)
# define dmawarn(fmt...) printk(KERN_DEBUG fmt)
# define dma_regaddr(chan, reg) ((chan)->regs + (reg))
# if 1
# define dma_wrreg(chan, reg, val) writel((val), (chan)->regs + (reg))
# else
static inline void
dma_wrreg ( struct s3c2410_dma_chan * chan , int reg , unsigned long val )
{
pr_debug ( " writing %08x to register %08x \n " , ( unsigned int ) val , reg ) ;
writel ( val , dma_regaddr ( chan , reg ) ) ;
}
# endif
# define dma_rdreg(chan, reg) readl((chan)->regs + (reg))
/* captured register state for debug */
struct s3c2410_dma_regstate {
unsigned long dcsrc ;
unsigned long disrc ;
unsigned long dstat ;
unsigned long dcon ;
unsigned long dmsktrig ;
} ;
# ifdef CONFIG_S3C2410_DMA_DEBUG
/* dmadbg_showregs
*
* simple debug routine to print the current state of the dma registers
*/
static void
dmadbg_capture ( struct s3c2410_dma_chan * chan , struct s3c2410_dma_regstate * regs )
{
regs - > dcsrc = dma_rdreg ( chan , S3C2410_DMA_DCSRC ) ;
regs - > disrc = dma_rdreg ( chan , S3C2410_DMA_DISRC ) ;
regs - > dstat = dma_rdreg ( chan , S3C2410_DMA_DSTAT ) ;
regs - > dcon = dma_rdreg ( chan , S3C2410_DMA_DCON ) ;
regs - > dmsktrig = dma_rdreg ( chan , S3C2410_DMA_DMASKTRIG ) ;
}
static void
dmadbg_dumpregs ( const char * fname , int line , struct s3c2410_dma_chan * chan ,
struct s3c2410_dma_regstate * regs )
{
printk ( KERN_DEBUG " dma%d: %s:%d: DCSRC=%08lx, DISRC=%08lx, DSTAT=%08lx DMT=%02lx, DCON=%08lx \n " ,
chan - > number , fname , line ,
regs - > dcsrc , regs - > disrc , regs - > dstat , regs - > dmsktrig ,
regs - > dcon ) ;
}
static void
dmadbg_showchan ( const char * fname , int line , struct s3c2410_dma_chan * chan )
{
struct s3c2410_dma_regstate state ;
dmadbg_capture ( chan , & state ) ;
printk ( KERN_DEBUG " dma%d: %s:%d: ls=%d, cur=%p, %p %p \n " ,
chan - > number , fname , line , chan - > load_state ,
chan - > curr , chan - > next , chan - > end ) ;
dmadbg_dumpregs ( fname , line , chan , & state ) ;
}
static void
dmadbg_showregs ( const char * fname , int line , struct s3c2410_dma_chan * chan )
{
struct s3c2410_dma_regstate state ;
dmadbg_capture ( chan , & state ) ;
dmadbg_dumpregs ( fname , line , chan , & state ) ;
}
2008-03-04 15:08:02 -08:00
# define dbg_showregs(chan) dmadbg_showregs(__func__, __LINE__, (chan))
# define dbg_showchan(chan) dmadbg_showchan(__func__, __LINE__, (chan))
2007-02-11 18:31:01 +01:00
# else
# define dbg_showregs(chan) do { } while(0)
# define dbg_showchan(chan) do { } while(0)
# endif /* CONFIG_S3C2410_DMA_DEBUG */
/* s3c2410_dma_stats_timeout
*
* Update DMA stats from timeout info
*/
static void
s3c2410_dma_stats_timeout ( struct s3c2410_dma_stats * stats , int val )
{
if ( stats = = NULL )
return ;
if ( val > stats - > timeout_longest )
stats - > timeout_longest = val ;
if ( val < stats - > timeout_shortest )
stats - > timeout_shortest = val ;
stats - > timeout_avg + = val ;
}
/* s3c2410_dma_waitforload
*
* wait for the DMA engine to load a buffer , and update the state accordingly
*/
static int
s3c2410_dma_waitforload ( struct s3c2410_dma_chan * chan , int line )
{
int timeout = chan - > load_timeout ;
int took ;
if ( chan - > load_state ! = S3C2410_DMALOAD_1LOADED ) {
printk ( KERN_ERR " dma%d: s3c2410_dma_waitforload() called in loadstate %d from line %d \n " , chan - > number , chan - > load_state , line ) ;
return 0 ;
}
if ( chan - > stats ! = NULL )
chan - > stats - > loads + + ;
while ( - - timeout > 0 ) {
if ( ( dma_rdreg ( chan , S3C2410_DMA_DSTAT ) < < ( 32 - 20 ) ) ! = 0 ) {
took = chan - > load_timeout - timeout ;
s3c2410_dma_stats_timeout ( chan - > stats , took ) ;
switch ( chan - > load_state ) {
case S3C2410_DMALOAD_1LOADED :
chan - > load_state = S3C2410_DMALOAD_1RUNNING ;
break ;
default :
printk ( KERN_ERR " dma%d: unknown load_state in s3c2410_dma_waitforload() %d \n " , chan - > number , chan - > load_state ) ;
}
return 1 ;
}
}
if ( chan - > stats ! = NULL ) {
chan - > stats - > timeout_failed + + ;
}
return 0 ;
}
/* s3c2410_dma_loadbuffer
*
* load a buffer , and update the channel state
*/
static inline int
s3c2410_dma_loadbuffer ( struct s3c2410_dma_chan * chan ,
struct s3c2410_dma_buf * buf )
{
unsigned long reload ;
if ( buf = = NULL ) {
dmawarn ( " buffer is NULL \n " ) ;
return - EINVAL ;
}
2009-10-17 08:42:49 +02:00
pr_debug ( " s3c2410_chan_loadbuffer: loading buff %p (0x%08lx,0x%06x) \n " ,
buf , ( unsigned long ) buf - > data , buf - > size ) ;
2007-02-11 18:31:01 +01:00
/* check the state of the channel before we do anything */
if ( chan - > load_state = = S3C2410_DMALOAD_1LOADED ) {
dmawarn ( " load_state is S3C2410_DMALOAD_1LOADED \n " ) ;
}
if ( chan - > load_state = = S3C2410_DMALOAD_1LOADED_1RUNNING ) {
dmawarn ( " state is S3C2410_DMALOAD_1LOADED_1RUNNING \n " ) ;
}
/* it would seem sensible if we are the last buffer to not bother
* with the auto - reload bit , so that the DMA engine will not try
* and load another transfer after this one has finished . . .
*/
if ( chan - > load_state = = S3C2410_DMALOAD_NONE ) {
pr_debug ( " load_state is none, checking for noreload (next=%p) \n " ,
buf - > next ) ;
reload = ( buf - > next = = NULL ) ? S3C2410_DCON_NORELOAD : 0 ;
} else {
//pr_debug("load_state is %d => autoreload\n", chan->load_state);
reload = S3C2410_DCON_AUTORELOAD ;
}
if ( ( buf - > data & 0xf0000000 ) ! = 0x30000000 ) {
dmawarn ( " dmaload: buffer is %p \n " , ( void * ) buf - > data ) ;
}
writel ( buf - > data , chan - > addr_reg ) ;
dma_wrreg ( chan , S3C2410_DMA_DCON ,
chan - > dcon | reload | ( buf - > size / chan - > xfer_unit ) ) ;
chan - > next = buf - > next ;
/* update the state of the channel */
switch ( chan - > load_state ) {
case S3C2410_DMALOAD_NONE :
chan - > load_state = S3C2410_DMALOAD_1LOADED ;
break ;
case S3C2410_DMALOAD_1RUNNING :
chan - > load_state = S3C2410_DMALOAD_1LOADED_1RUNNING ;
break ;
default :
dmawarn ( " dmaload: unknown state %d in loadbuffer \n " ,
chan - > load_state ) ;
break ;
}
return 0 ;
}
/* s3c2410_dma_call_op
*
* small routine to call the op routine with the given op if it has been
* registered
*/
static void
s3c2410_dma_call_op ( struct s3c2410_dma_chan * chan , enum s3c2410_chan_op op )
{
if ( chan - > op_fn ! = NULL ) {
( chan - > op_fn ) ( chan , op ) ;
}
}
/* s3c2410_dma_buffdone
*
* small wrapper to check if callback routine needs to be called , and
* if so , call it
*/
static inline void
s3c2410_dma_buffdone ( struct s3c2410_dma_chan * chan , struct s3c2410_dma_buf * buf ,
enum s3c2410_dma_buffresult result )
{
#if 0
pr_debug ( " callback_fn=%p, buf=%p, id=%p, size=%d, result=%d \n " ,
chan - > callback_fn , buf , buf - > id , buf - > size , result ) ;
# endif
if ( chan - > callback_fn ! = NULL ) {
( chan - > callback_fn ) ( chan , buf - > id , buf - > size , result ) ;
}
}
/* s3c2410_dma_start
*
* start a dma channel going
*/
static int s3c2410_dma_start ( struct s3c2410_dma_chan * chan )
{
unsigned long tmp ;
unsigned long flags ;
pr_debug ( " s3c2410_start_dma: channel=%d \n " , chan - > number ) ;
local_irq_save ( flags ) ;
if ( chan - > state = = S3C2410_DMA_RUNNING ) {
pr_debug ( " s3c2410_start_dma: already running (%d) \n " , chan - > state ) ;
local_irq_restore ( flags ) ;
return 0 ;
}
chan - > state = S3C2410_DMA_RUNNING ;
2012-09-19 21:48:00 -04:00
/* check whether there is anything to load, and if not, see
2007-02-11 18:31:01 +01:00
* if we can find anything to load
*/
if ( chan - > load_state = = S3C2410_DMALOAD_NONE ) {
if ( chan - > next = = NULL ) {
printk ( KERN_ERR " dma%d: channel has nothing loaded \n " ,
chan - > number ) ;
chan - > state = S3C2410_DMA_IDLE ;
local_irq_restore ( flags ) ;
return - EINVAL ;
}
s3c2410_dma_loadbuffer ( chan , chan - > next ) ;
}
dbg_showchan ( chan ) ;
/* enable the channel */
if ( ! chan - > irq_enabled ) {
enable_irq ( chan - > irq ) ;
chan - > irq_enabled = 1 ;
}
/* start the channel going */
tmp = dma_rdreg ( chan , S3C2410_DMA_DMASKTRIG ) ;
tmp & = ~ S3C2410_DMASKTRIG_STOP ;
tmp | = S3C2410_DMASKTRIG_ON ;
dma_wrreg ( chan , S3C2410_DMA_DMASKTRIG , tmp ) ;
pr_debug ( " dma%d: %08lx to DMASKTRIG \n " , chan - > number , tmp ) ;
#if 0
/* the dma buffer loads should take care of clearing the AUTO
* reloading feature */
tmp = dma_rdreg ( chan , S3C2410_DMA_DCON ) ;
tmp & = ~ S3C2410_DCON_NORELOAD ;
dma_wrreg ( chan , S3C2410_DMA_DCON , tmp ) ;
# endif
s3c2410_dma_call_op ( chan , S3C2410_DMAOP_START ) ;
dbg_showchan ( chan ) ;
/* if we've only loaded one buffer onto the channel, then chec
* to see if we have another , and if so , try and load it so when
* the first buffer is finished , the new one will be loaded onto
* the channel */
if ( chan - > next ! = NULL ) {
if ( chan - > load_state = = S3C2410_DMALOAD_1LOADED ) {
if ( s3c2410_dma_waitforload ( chan , __LINE__ ) = = 0 ) {
pr_debug ( " %s: buff not yet loaded, no more todo \n " ,
2008-03-04 15:08:02 -08:00
__func__ ) ;
2007-02-11 18:31:01 +01:00
} else {
chan - > load_state = S3C2410_DMALOAD_1RUNNING ;
s3c2410_dma_loadbuffer ( chan , chan - > next ) ;
}
} else if ( chan - > load_state = = S3C2410_DMALOAD_1RUNNING ) {
s3c2410_dma_loadbuffer ( chan , chan - > next ) ;
}
}
local_irq_restore ( flags ) ;
return 0 ;
}
/* s3c2410_dma_canload
*
* work out if we can queue another buffer into the DMA engine
*/
static int
s3c2410_dma_canload ( struct s3c2410_dma_chan * chan )
{
if ( chan - > load_state = = S3C2410_DMALOAD_NONE | |
chan - > load_state = = S3C2410_DMALOAD_1RUNNING )
return 1 ;
return 0 ;
}
/* s3c2410_dma_enqueue
*
* queue an given buffer for dma transfer .
*
* id the device driver ' s id information for this buffer
* data the physical address of the buffer data
* size the size of the buffer in bytes
*
* If the channel is not running , then the flag S3C2410_DMAF_AUTOSTART
* is checked , and if set , the channel is started . If this flag isn ' t set ,
* then an error will be returned .
*
* It is possible to queue more than one DMA buffer onto a channel at
* once , and the code will deal with the re - loading of the next buffer
* when necessary .
*/
2012-08-07 19:12:05 +09:00
int s3c2410_dma_enqueue ( enum dma_ch channel , void * id ,
2007-02-11 18:31:01 +01:00
dma_addr_t data , int size )
{
2009-03-19 15:02:39 +00:00
struct s3c2410_dma_chan * chan = s3c_dma_lookup_channel ( channel ) ;
2007-02-11 18:31:01 +01:00
struct s3c2410_dma_buf * buf ;
unsigned long flags ;
if ( chan = = NULL )
return - EINVAL ;
pr_debug ( " %s: id=%p, data=%08x, size=%d \n " ,
2008-03-04 15:08:02 -08:00
__func__ , id , ( unsigned int ) data , size ) ;
2007-02-11 18:31:01 +01:00
buf = kmem_cache_alloc ( dma_kmem , GFP_ATOMIC ) ;
if ( buf = = NULL ) {
pr_debug ( " %s: out of memory (%ld alloc) \n " ,
2008-03-04 15:08:02 -08:00
__func__ , ( long ) sizeof ( * buf ) ) ;
2007-02-11 18:31:01 +01:00
return - ENOMEM ;
}
2008-03-04 15:08:02 -08:00
//pr_debug("%s: new buffer %p\n", __func__, buf);
2007-02-11 18:31:01 +01:00
//dbg_showchan(chan);
buf - > next = NULL ;
buf - > data = buf - > ptr = data ;
buf - > size = size ;
buf - > id = id ;
buf - > magic = BUF_MAGIC ;
local_irq_save ( flags ) ;
if ( chan - > curr = = NULL ) {
/* we've got nothing loaded... */
pr_debug ( " %s: buffer %p queued onto empty channel \n " ,
2008-03-04 15:08:02 -08:00
__func__ , buf ) ;
2007-02-11 18:31:01 +01:00
chan - > curr = buf ;
chan - > end = buf ;
chan - > next = NULL ;
} else {
pr_debug ( " dma%d: %s: buffer %p queued onto non-empty channel \n " ,
2008-03-04 15:08:02 -08:00
chan - > number , __func__ , buf ) ;
2007-02-11 18:31:01 +01:00
2012-11-22 00:11:09 +09:00
if ( chan - > end = = NULL ) {
2007-02-11 18:31:01 +01:00
pr_debug ( " dma%d: %s: %p not empty, and chan->end==NULL? \n " ,
2008-03-04 15:08:02 -08:00
chan - > number , __func__ , chan ) ;
2012-11-22 00:11:09 +09:00
} else {
chan - > end - > next = buf ;
chan - > end = buf ;
}
2007-02-11 18:31:01 +01:00
}
/* if necessary, update the next buffer field */
if ( chan - > next = = NULL )
chan - > next = buf ;
/* check to see if we can load a buffer */
if ( chan - > state = = S3C2410_DMA_RUNNING ) {
if ( chan - > load_state = = S3C2410_DMALOAD_1LOADED & & 1 ) {
if ( s3c2410_dma_waitforload ( chan , __LINE__ ) = = 0 ) {
printk ( KERN_ERR " dma%d: loadbuffer: "
" timeout loading buffer \n " ,
chan - > number ) ;
dbg_showchan ( chan ) ;
local_irq_restore ( flags ) ;
return - EINVAL ;
}
}
while ( s3c2410_dma_canload ( chan ) & & chan - > next ! = NULL ) {
s3c2410_dma_loadbuffer ( chan , chan - > next ) ;
}
} else if ( chan - > state = = S3C2410_DMA_IDLE ) {
if ( chan - > flags & S3C2410_DMAF_AUTOSTART ) {
2008-01-28 13:01:28 +01:00
s3c2410_dma_ctrl ( chan - > number | DMACH_LOW_LEVEL ,
S3C2410_DMAOP_START ) ;
2007-02-11 18:31:01 +01:00
}
}
local_irq_restore ( flags ) ;
return 0 ;
}
EXPORT_SYMBOL ( s3c2410_dma_enqueue ) ;
static inline void
s3c2410_dma_freebuf ( struct s3c2410_dma_buf * buf )
{
int magicok = ( buf - > magic = = BUF_MAGIC ) ;
buf - > magic = - 1 ;
if ( magicok ) {
kmem_cache_free ( dma_kmem , buf ) ;
} else {
printk ( " s3c2410_dma_freebuf: buff %p with bad magic \n " , buf ) ;
}
}
/* s3c2410_dma_lastxfer
*
* called when the system is out of buffers , to ensure that the channel
* is prepared for shutdown .
*/
static inline void
s3c2410_dma_lastxfer ( struct s3c2410_dma_chan * chan )
{
#if 0
pr_debug ( " dma%d: s3c2410_dma_lastxfer: load_state %d \n " ,
chan - > number , chan - > load_state ) ;
# endif
switch ( chan - > load_state ) {
case S3C2410_DMALOAD_NONE :
break ;
case S3C2410_DMALOAD_1LOADED :
if ( s3c2410_dma_waitforload ( chan , __LINE__ ) = = 0 ) {
/* flag error? */
printk ( KERN_ERR " dma%d: timeout waiting for load (%s) \n " ,
2008-03-04 15:08:02 -08:00
chan - > number , __func__ ) ;
2007-02-11 18:31:01 +01:00
return ;
}
break ;
case S3C2410_DMALOAD_1LOADED_1RUNNING :
2011-03-30 22:57:33 -03:00
/* I believe in this case we do not have anything to do
2007-02-11 18:31:01 +01:00
* until the next buffer comes along , and we turn off the
* reload */
return ;
default :
pr_debug ( " dma%d: lastxfer: unhandled load_state %d with no next \n " ,
chan - > number , chan - > load_state ) ;
return ;
}
/* hopefully this'll shut the damned thing up after the transfer... */
dma_wrreg ( chan , S3C2410_DMA_DCON , chan - > dcon | S3C2410_DCON_NORELOAD ) ;
}
# define dmadbg2(x...)
static irqreturn_t
s3c2410_dma_irq ( int irq , void * devpw )
{
struct s3c2410_dma_chan * chan = ( struct s3c2410_dma_chan * ) devpw ;
struct s3c2410_dma_buf * buf ;
buf = chan - > curr ;
dbg_showchan ( chan ) ;
/* modify the channel state */
switch ( chan - > load_state ) {
case S3C2410_DMALOAD_1RUNNING :
/* TODO - if we are running only one buffer, we probably
* want to reload here , and then worry about the buffer
* callback */
chan - > load_state = S3C2410_DMALOAD_NONE ;
break ;
case S3C2410_DMALOAD_1LOADED :
/* iirc, we should go back to NONE loaded here, we
* had a buffer , and it was never verified as being
* loaded .
*/
chan - > load_state = S3C2410_DMALOAD_NONE ;
break ;
case S3C2410_DMALOAD_1LOADED_1RUNNING :
/* we'll worry about checking to see if another buffer is
* ready after we ' ve called back the owner . This should
* ensure we do not wait around too long for the DMA
* engine to start the next transfer
*/
chan - > load_state = S3C2410_DMALOAD_1LOADED ;
break ;
case S3C2410_DMALOAD_NONE :
printk ( KERN_ERR " dma%d: IRQ with no loaded buffer? \n " ,
chan - > number ) ;
break ;
default :
printk ( KERN_ERR " dma%d: IRQ in invalid load_state %d \n " ,
chan - > number , chan - > load_state ) ;
break ;
}
if ( buf ! = NULL ) {
/* update the chain to make sure that if we load any more
* buffers when we call the callback function , things should
* work properly */
chan - > curr = buf - > next ;
buf - > next = NULL ;
if ( buf - > magic ! = BUF_MAGIC ) {
printk ( KERN_ERR " dma%d: %s: buf %p incorrect magic \n " ,
2008-03-04 15:08:02 -08:00
chan - > number , __func__ , buf ) ;
2007-02-11 18:31:01 +01:00
return IRQ_HANDLED ;
}
s3c2410_dma_buffdone ( chan , buf , S3C2410_RES_OK ) ;
/* free resouces */
s3c2410_dma_freebuf ( buf ) ;
} else {
}
/* only reload if the channel is still running... our buffer done
* routine may have altered the state by requesting the dma channel
* to stop or shutdown . . . */
/* todo: check that when the channel is shut-down from inside this
* function , we cope with unsetting reload , etc */
if ( chan - > next ! = NULL & & chan - > state ! = S3C2410_DMA_IDLE ) {
unsigned long flags ;
switch ( chan - > load_state ) {
case S3C2410_DMALOAD_1RUNNING :
/* don't need to do anything for this state */
break ;
case S3C2410_DMALOAD_NONE :
/* can load buffer immediately */
break ;
case S3C2410_DMALOAD_1LOADED :
if ( s3c2410_dma_waitforload ( chan , __LINE__ ) = = 0 ) {
/* flag error? */
printk ( KERN_ERR " dma%d: timeout waiting for load (%s) \n " ,
2008-03-04 15:08:02 -08:00
chan - > number , __func__ ) ;
2007-02-11 18:31:01 +01:00
return IRQ_HANDLED ;
}
break ;
case S3C2410_DMALOAD_1LOADED_1RUNNING :
goto no_load ;
default :
printk ( KERN_ERR " dma%d: unknown load_state in irq, %d \n " ,
chan - > number , chan - > load_state ) ;
return IRQ_HANDLED ;
}
local_irq_save ( flags ) ;
s3c2410_dma_loadbuffer ( chan , chan - > next ) ;
local_irq_restore ( flags ) ;
} else {
s3c2410_dma_lastxfer ( chan ) ;
/* see if we can stop this channel.. */
if ( chan - > load_state = = S3C2410_DMALOAD_NONE ) {
pr_debug ( " dma%d: end of transfer, stopping channel (%ld) \n " ,
chan - > number , jiffies ) ;
s3c2410_dma_ctrl ( chan - > number | DMACH_LOW_LEVEL ,
S3C2410_DMAOP_STOP ) ;
}
}
no_load :
return IRQ_HANDLED ;
}
static struct s3c2410_dma_chan * s3c2410_dma_map_channel ( int channel ) ;
/* s3c2410_request_dma
*
* get control of an dma channel
*/
2011-07-16 15:50:19 +09:00
int s3c2410_dma_request ( enum dma_ch channel ,
2007-02-11 18:31:01 +01:00
struct s3c2410_dma_client * client ,
void * dev )
{
struct s3c2410_dma_chan * chan ;
unsigned long flags ;
int err ;
pr_debug ( " dma%d: s3c2410_request_dma: client=%s, dev=%p \n " ,
channel , client - > name , dev ) ;
local_irq_save ( flags ) ;
chan = s3c2410_dma_map_channel ( channel ) ;
if ( chan = = NULL ) {
local_irq_restore ( flags ) ;
return - EBUSY ;
}
dbg_showchan ( chan ) ;
chan - > client = client ;
chan - > in_use = 1 ;
if ( ! chan - > irq_claimed ) {
pr_debug ( " dma%d: %s : requesting irq %d \n " ,
2008-03-04 15:08:02 -08:00
channel , __func__ , chan - > irq ) ;
2007-02-11 18:31:01 +01:00
chan - > irq_claimed = 1 ;
local_irq_restore ( flags ) ;
err = request_irq ( chan - > irq , s3c2410_dma_irq , IRQF_DISABLED ,
client - > name , ( void * ) chan ) ;
local_irq_save ( flags ) ;
if ( err ) {
chan - > in_use = 0 ;
chan - > irq_claimed = 0 ;
local_irq_restore ( flags ) ;
printk ( KERN_ERR " %s: cannot get IRQ %d for DMA %d \n " ,
client - > name , chan - > irq , chan - > number ) ;
return err ;
}
chan - > irq_enabled = 1 ;
}
local_irq_restore ( flags ) ;
/* need to setup */
2008-03-04 15:08:02 -08:00
pr_debug ( " %s: channel initialised, %p \n " , __func__ , chan ) ;
2007-02-11 18:31:01 +01:00
2008-01-28 13:01:27 +01:00
return chan - > number | DMACH_LOW_LEVEL ;
2007-02-11 18:31:01 +01:00
}
EXPORT_SYMBOL ( s3c2410_dma_request ) ;
/* s3c2410_dma_free
*
* release the given channel back to the system , will stop and flush
* any outstanding transfers , and ensure the channel is ready for the
* next claimant .
*
* Note , although a warning is currently printed if the freeing client
* info is not the same as the registrant ' s client info , the free is still
* allowed to go through .
*/
2011-07-16 15:50:19 +09:00
int s3c2410_dma_free ( enum dma_ch channel , struct s3c2410_dma_client * client )
2007-02-11 18:31:01 +01:00
{
2009-03-19 15:02:39 +00:00
struct s3c2410_dma_chan * chan = s3c_dma_lookup_channel ( channel ) ;
2007-02-11 18:31:01 +01:00
unsigned long flags ;
if ( chan = = NULL )
return - EINVAL ;
local_irq_save ( flags ) ;
if ( chan - > client ! = client ) {
printk ( KERN_WARNING " dma%d: possible free from different client (channel %p, passed %p) \n " ,
channel , chan - > client , client ) ;
}
/* sort out stopping and freeing the channel */
if ( chan - > state ! = S3C2410_DMA_IDLE ) {
pr_debug ( " %s: need to stop dma channel %p \n " ,
2008-03-04 15:08:02 -08:00
__func__ , chan ) ;
2007-02-11 18:31:01 +01:00
/* possibly flush the channel */
s3c2410_dma_ctrl ( channel , S3C2410_DMAOP_STOP ) ;
}
chan - > client = NULL ;
chan - > in_use = 0 ;
if ( chan - > irq_claimed )
free_irq ( chan - > irq , ( void * ) chan ) ;
chan - > irq_claimed = 0 ;
if ( ! ( channel & DMACH_LOW_LEVEL ) )
2009-03-19 15:02:39 +00:00
s3c_dma_chan_map [ channel ] = NULL ;
2007-02-11 18:31:01 +01:00
local_irq_restore ( flags ) ;
return 0 ;
}
EXPORT_SYMBOL ( s3c2410_dma_free ) ;
static int s3c2410_dma_dostop ( struct s3c2410_dma_chan * chan )
{
unsigned long flags ;
unsigned long tmp ;
2008-03-04 15:08:02 -08:00
pr_debug ( " %s: \n " , __func__ ) ;
2007-02-11 18:31:01 +01:00
dbg_showchan ( chan ) ;
local_irq_save ( flags ) ;
s3c2410_dma_call_op ( chan , S3C2410_DMAOP_STOP ) ;
tmp = dma_rdreg ( chan , S3C2410_DMA_DMASKTRIG ) ;
tmp | = S3C2410_DMASKTRIG_STOP ;
//tmp &= ~S3C2410_DMASKTRIG_ON;
dma_wrreg ( chan , S3C2410_DMA_DMASKTRIG , tmp ) ;
#if 0
/* should also clear interrupts, according to WinCE BSP */
tmp = dma_rdreg ( chan , S3C2410_DMA_DCON ) ;
tmp | = S3C2410_DCON_NORELOAD ;
dma_wrreg ( chan , S3C2410_DMA_DCON , tmp ) ;
# endif
/* should stop do this, or should we wait for flush? */
chan - > state = S3C2410_DMA_IDLE ;
chan - > load_state = S3C2410_DMALOAD_NONE ;
local_irq_restore ( flags ) ;
return 0 ;
}
2007-04-20 11:39:46 +01:00
static void s3c2410_dma_waitforstop ( struct s3c2410_dma_chan * chan )
2007-02-11 18:31:01 +01:00
{
unsigned long tmp ;
unsigned int timeout = 0x10000 ;
while ( timeout - - > 0 ) {
tmp = dma_rdreg ( chan , S3C2410_DMA_DMASKTRIG ) ;
if ( ! ( tmp & S3C2410_DMASKTRIG_ON ) )
return ;
}
pr_debug ( " dma%d: failed to stop? \n " , chan - > number ) ;
}
/* s3c2410_dma_flush
*
* stop the channel , and remove all current and pending transfers
*/
static int s3c2410_dma_flush ( struct s3c2410_dma_chan * chan )
{
struct s3c2410_dma_buf * buf , * next ;
unsigned long flags ;
2008-03-04 15:08:02 -08:00
pr_debug ( " %s: chan %p (%d) \n " , __func__ , chan , chan - > number ) ;
2007-02-11 18:31:01 +01:00
dbg_showchan ( chan ) ;
local_irq_save ( flags ) ;
if ( chan - > state ! = S3C2410_DMA_IDLE ) {
2008-03-04 15:08:02 -08:00
pr_debug ( " %s: stopping channel... \n " , __func__ ) ;
2007-02-11 18:31:01 +01:00
s3c2410_dma_ctrl ( chan - > number , S3C2410_DMAOP_STOP ) ;
}
buf = chan - > curr ;
if ( buf = = NULL )
buf = chan - > next ;
chan - > curr = chan - > next = chan - > end = NULL ;
if ( buf ! = NULL ) {
for ( ; buf ! = NULL ; buf = next ) {
next = buf - > next ;
pr_debug ( " %s: free buffer %p, next %p \n " ,
2008-03-04 15:08:02 -08:00
__func__ , buf , buf - > next ) ;
2007-02-11 18:31:01 +01:00
s3c2410_dma_buffdone ( chan , buf , S3C2410_RES_ABORT ) ;
s3c2410_dma_freebuf ( buf ) ;
}
}
dbg_showregs ( chan ) ;
s3c2410_dma_waitforstop ( chan ) ;
#if 0
/* should also clear interrupts, according to WinCE BSP */
{
unsigned long tmp ;
tmp = dma_rdreg ( chan , S3C2410_DMA_DCON ) ;
tmp | = S3C2410_DCON_NORELOAD ;
dma_wrreg ( chan , S3C2410_DMA_DCON , tmp ) ;
}
# endif
dbg_showregs ( chan ) ;
local_irq_restore ( flags ) ;
return 0 ;
}
2007-04-20 11:39:46 +01:00
static int s3c2410_dma_started ( struct s3c2410_dma_chan * chan )
2007-02-11 18:31:01 +01:00
{
unsigned long flags ;
local_irq_save ( flags ) ;
dbg_showchan ( chan ) ;
/* if we've only loaded one buffer onto the channel, then chec
* to see if we have another , and if so , try and load it so when
* the first buffer is finished , the new one will be loaded onto
* the channel */
if ( chan - > next ! = NULL ) {
if ( chan - > load_state = = S3C2410_DMALOAD_1LOADED ) {
if ( s3c2410_dma_waitforload ( chan , __LINE__ ) = = 0 ) {
pr_debug ( " %s: buff not yet loaded, no more todo \n " ,
2008-03-04 15:08:02 -08:00
__func__ ) ;
2007-02-11 18:31:01 +01:00
} else {
chan - > load_state = S3C2410_DMALOAD_1RUNNING ;
s3c2410_dma_loadbuffer ( chan , chan - > next ) ;
}
} else if ( chan - > load_state = = S3C2410_DMALOAD_1RUNNING ) {
s3c2410_dma_loadbuffer ( chan , chan - > next ) ;
}
}
local_irq_restore ( flags ) ;
return 0 ;
}
int
2011-07-16 15:50:19 +09:00
s3c2410_dma_ctrl ( enum dma_ch channel , enum s3c2410_chan_op op )
2007-02-11 18:31:01 +01:00
{
2009-03-19 15:02:39 +00:00
struct s3c2410_dma_chan * chan = s3c_dma_lookup_channel ( channel ) ;
2007-02-11 18:31:01 +01:00
if ( chan = = NULL )
return - EINVAL ;
switch ( op ) {
case S3C2410_DMAOP_START :
return s3c2410_dma_start ( chan ) ;
case S3C2410_DMAOP_STOP :
return s3c2410_dma_dostop ( chan ) ;
case S3C2410_DMAOP_PAUSE :
case S3C2410_DMAOP_RESUME :
return - ENOENT ;
case S3C2410_DMAOP_FLUSH :
return s3c2410_dma_flush ( chan ) ;
case S3C2410_DMAOP_STARTED :
return s3c2410_dma_started ( chan ) ;
case S3C2410_DMAOP_TIMEOUT :
return 0 ;
}
return - ENOENT ; /* unknown, don't bother */
}
EXPORT_SYMBOL ( s3c2410_dma_ctrl ) ;
/* DMA configuration for each channel
*
* DISRCC - > source of the DMA ( AHB , APB )
* DISRC - > source address of the DMA
* DIDSTC - > destination of the DMA ( AHB , APD )
* DIDST - > destination address of the DMA
*/
/* s3c2410_dma_config
*
* xfersize : size of unit in bytes ( 1 , 2 , 4 )
*/
2011-07-16 15:50:19 +09:00
int s3c2410_dma_config ( enum dma_ch channel ,
2009-03-19 15:02:34 +00:00
int xferunit )
2007-02-11 18:31:01 +01:00
{
2009-03-19 15:02:39 +00:00
struct s3c2410_dma_chan * chan = s3c_dma_lookup_channel ( channel ) ;
2009-03-19 15:02:34 +00:00
unsigned int dcon ;
2007-02-11 18:31:01 +01:00
2011-07-08 14:33:02 +09:00
pr_debug ( " %s: chan=%d, xfer_unit=%d \n " , __func__ , channel , xferunit ) ;
2007-02-11 18:31:01 +01:00
if ( chan = = NULL )
return - EINVAL ;
2009-03-19 15:02:34 +00:00
dcon = chan - > dcon & dma_sel . dcon_mask ;
2011-07-08 14:33:02 +09:00
pr_debug ( " %s: dcon is %08x \n " , __func__ , dcon ) ;
2007-02-11 18:31:01 +01:00
2009-03-19 15:02:34 +00:00
switch ( chan - > req_ch ) {
case DMACH_I2S_IN :
case DMACH_I2S_OUT :
case DMACH_PCM_IN :
case DMACH_PCM_OUT :
case DMACH_MIC_IN :
default :
dcon | = S3C2410_DCON_HANDSHAKE ;
dcon | = S3C2410_DCON_SYNC_PCLK ;
break ;
case DMACH_SDI :
/* note, ensure if need HANDSHAKE or not */
dcon | = S3C2410_DCON_SYNC_PCLK ;
break ;
case DMACH_XD0 :
case DMACH_XD1 :
dcon | = S3C2410_DCON_HANDSHAKE ;
dcon | = S3C2410_DCON_SYNC_HCLK ;
break ;
}
2007-02-11 18:31:01 +01:00
switch ( xferunit ) {
case 1 :
dcon | = S3C2410_DCON_BYTE ;
break ;
case 2 :
dcon | = S3C2410_DCON_HALFWORD ;
break ;
case 4 :
dcon | = S3C2410_DCON_WORD ;
break ;
default :
2008-03-04 15:08:02 -08:00
pr_debug ( " %s: bad transfer size %d \n " , __func__ , xferunit ) ;
2007-02-11 18:31:01 +01:00
return - EINVAL ;
}
dcon | = S3C2410_DCON_HWTRIG ;
dcon | = S3C2410_DCON_INTREQ ;
2008-03-04 15:08:02 -08:00
pr_debug ( " %s: dcon now %08x \n " , __func__ , dcon ) ;
2007-02-11 18:31:01 +01:00
chan - > dcon = dcon ;
chan - > xfer_unit = xferunit ;
return 0 ;
}
EXPORT_SYMBOL ( s3c2410_dma_config ) ;
/* s3c2410_dma_devconfig
*
* configure the dma source / destination hardware type and address
*
2011-09-02 09:44:44 +09:00
* source : DMA_FROM_DEVICE : source is hardware
* DMA_TO_DEVICE : source is memory
2007-02-11 18:31:01 +01:00
*
* devaddr : physical address of the source
*/
2011-07-16 15:50:19 +09:00
int s3c2410_dma_devconfig ( enum dma_ch channel ,
2011-09-02 09:44:44 +09:00
enum dma_data_direction source ,
2007-02-11 18:31:01 +01:00
unsigned long devaddr )
{
2009-03-19 15:02:39 +00:00
struct s3c2410_dma_chan * chan = s3c_dma_lookup_channel ( channel ) ;
2009-03-19 15:02:34 +00:00
unsigned int hwcfg ;
2007-02-11 18:31:01 +01:00
if ( chan = = NULL )
return - EINVAL ;
2009-03-19 15:02:34 +00:00
pr_debug ( " %s: source=%d, devaddr=%08lx \n " ,
__func__ , ( int ) source , devaddr ) ;
2007-02-11 18:31:01 +01:00
chan - > source = source ;
chan - > dev_addr = devaddr ;
2009-03-19 15:02:34 +00:00
switch ( chan - > req_ch ) {
case DMACH_XD0 :
case DMACH_XD1 :
hwcfg = 0 ; /* AHB */
break ;
default :
hwcfg = S3C2410_DISRCC_APB ;
}
/* always assume our peripheral desintation is a fixed
* address in memory . */
hwcfg | = S3C2410_DISRCC_INC ;
2007-02-11 18:31:01 +01:00
switch ( source ) {
2011-09-02 09:44:44 +09:00
case DMA_FROM_DEVICE :
2007-02-11 18:31:01 +01:00
/* source is hardware */
pr_debug ( " %s: hw source, devaddr=%08lx, hwcfg=%d \n " ,
2008-03-04 15:08:02 -08:00
__func__ , devaddr , hwcfg ) ;
2007-02-11 18:31:01 +01:00
dma_wrreg ( chan , S3C2410_DMA_DISRCC , hwcfg & 3 ) ;
dma_wrreg ( chan , S3C2410_DMA_DISRC , devaddr ) ;
dma_wrreg ( chan , S3C2410_DMA_DIDSTC , ( 0 < < 1 ) | ( 0 < < 0 ) ) ;
chan - > addr_reg = dma_regaddr ( chan , S3C2410_DMA_DIDST ) ;
2008-01-28 13:01:20 +01:00
break ;
2007-02-11 18:31:01 +01:00
2011-09-02 09:44:44 +09:00
case DMA_TO_DEVICE :
2007-02-11 18:31:01 +01:00
/* source is memory */
2008-03-04 15:08:02 -08:00
pr_debug ( " %s: mem source, devaddr=%08lx, hwcfg=%d \n " ,
__func__ , devaddr , hwcfg ) ;
2007-02-11 18:31:01 +01:00
dma_wrreg ( chan , S3C2410_DMA_DISRCC , ( 0 < < 1 ) | ( 0 < < 0 ) ) ;
dma_wrreg ( chan , S3C2410_DMA_DIDST , devaddr ) ;
dma_wrreg ( chan , S3C2410_DMA_DIDSTC , hwcfg & 3 ) ;
chan - > addr_reg = dma_regaddr ( chan , S3C2410_DMA_DISRC ) ;
2008-01-28 13:01:20 +01:00
break ;
default :
printk ( KERN_ERR " dma%d: invalid source type (%d) \n " ,
channel , source ) ;
return - EINVAL ;
2007-02-11 18:31:01 +01:00
}
2008-01-28 13:01:20 +01:00
return 0 ;
2007-02-11 18:31:01 +01:00
}
EXPORT_SYMBOL ( s3c2410_dma_devconfig ) ;
/* s3c2410_dma_getposition
*
* returns the current transfer points for the dma source and destination
*/
2011-07-16 15:50:19 +09:00
int s3c2410_dma_getposition ( enum dma_ch channel , dma_addr_t * src , dma_addr_t * dst )
2007-02-11 18:31:01 +01:00
{
2009-03-19 15:02:39 +00:00
struct s3c2410_dma_chan * chan = s3c_dma_lookup_channel ( channel ) ;
2007-02-11 18:31:01 +01:00
if ( chan = = NULL )
return - EINVAL ;
if ( src ! = NULL )
* src = dma_rdreg ( chan , S3C2410_DMA_DCSRC ) ;
if ( dst ! = NULL )
* dst = dma_rdreg ( chan , S3C2410_DMA_DCDST ) ;
return 0 ;
}
EXPORT_SYMBOL ( s3c2410_dma_getposition ) ;
2011-04-22 22:03:21 +02:00
/* system core operations */
2007-02-11 18:31:01 +01:00
# ifdef CONFIG_PM
2011-06-02 12:56:39 +02:00
static void s3c2410_dma_suspend_chan ( struct s3c2410_dma_chan * cp )
2007-02-11 18:31:01 +01:00
{
printk ( KERN_DEBUG " suspending dma channel %d \n " , cp - > number ) ;
if ( dma_rdreg ( cp , S3C2410_DMA_DMASKTRIG ) & S3C2410_DMASKTRIG_ON ) {
/* the dma channel is still working, which is probably
* a bad thing to do over suspend / resume . We stop the
* channel and assume that the client is either going to
* retry after resume , or that it is broken .
*/
printk ( KERN_INFO " dma: stopping channel %d due to suspend \n " ,
cp - > number ) ;
s3c2410_dma_dostop ( cp ) ;
}
2011-04-22 22:03:21 +02:00
}
static int s3c2410_dma_suspend ( void )
{
struct s3c2410_dma_chan * cp = s3c2410_chans ;
int channel ;
for ( channel = 0 ; channel < dma_channels ; cp + + , channel + + )
s3c2410_dma_suspend_chan ( cp ) ;
2007-02-11 18:31:01 +01:00
return 0 ;
}
2011-04-22 22:03:21 +02:00
static void s3c2410_dma_resume_chan ( struct s3c2410_dma_chan * cp )
2007-02-11 18:31:01 +01:00
{
2008-01-28 13:01:21 +01:00
unsigned int no = cp - > number | DMACH_LOW_LEVEL ;
/* restore channel's hardware configuration */
if ( ! cp - > in_use )
2011-07-08 14:33:02 +09:00
return ;
2008-01-28 13:01:21 +01:00
printk ( KERN_INFO " dma%d: restoring configuration \n " , cp - > number ) ;
2009-03-19 15:02:34 +00:00
s3c2410_dma_config ( no , cp - > xfer_unit ) ;
s3c2410_dma_devconfig ( no , cp - > source , cp - > dev_addr ) ;
2008-01-28 13:01:21 +01:00
/* re-select the dma source for this channel */
if ( cp - > map ! = NULL )
dma_sel . select ( cp , cp - > map ) ;
2007-02-11 18:31:01 +01:00
}
2011-04-22 22:03:21 +02:00
static void s3c2410_dma_resume ( void )
{
struct s3c2410_dma_chan * cp = s3c2410_chans + dma_channels - 1 ;
int channel ;
2012-03-03 07:32:36 +09:00
for ( channel = dma_channels - 1 ; channel > = 0 ; cp - - , channel - - )
2011-04-22 22:03:21 +02:00
s3c2410_dma_resume_chan ( cp ) ;
}
2007-02-11 18:31:01 +01:00
# else
# define s3c2410_dma_suspend NULL
# define s3c2410_dma_resume NULL
# endif /* CONFIG_PM */
2011-04-22 22:03:21 +02:00
struct syscore_ops dma_syscore_ops = {
2007-02-11 18:31:01 +01:00
. suspend = s3c2410_dma_suspend ,
. resume = s3c2410_dma_resume ,
} ;
/* kmem cache implementation */
2008-07-25 19:45:34 -07:00
static void s3c2410_dma_cache_ctor ( void * p )
2007-02-11 18:31:01 +01:00
{
memset ( p , 0 , sizeof ( struct s3c2410_dma_buf ) ) ;
}
/* initialisation code */
2011-04-22 22:03:21 +02:00
static int __init s3c24xx_dma_syscore_init ( void )
2007-02-17 15:37:14 +01:00
{
2011-04-22 22:03:21 +02:00
register_syscore_ops ( & dma_syscore_ops ) ;
2007-02-17 15:37:14 +01:00
return 0 ;
}
2011-04-22 22:03:21 +02:00
late_initcall ( s3c24xx_dma_syscore_init ) ;
2007-02-17 15:37:14 +01:00
int __init s3c24xx_dma_init ( unsigned int channels , unsigned int irq ,
unsigned int stride )
2007-02-11 18:31:01 +01:00
{
struct s3c2410_dma_chan * cp ;
int channel ;
int ret ;
2009-11-13 22:54:13 +00:00
printk ( " S3C24XX DMA Driver, Copyright 2003-2006 Simtec Electronics \n " ) ;
2007-02-11 18:31:01 +01:00
2007-02-17 15:37:14 +01:00
dma_channels = channels ;
dma_base = ioremap ( S3C24XX_PA_DMA , stride * channels ) ;
2007-02-11 18:31:01 +01:00
if ( dma_base = = NULL ) {
printk ( KERN_ERR " dma failed to remap register block \n " ) ;
return - ENOMEM ;
}
2007-02-17 15:37:14 +01:00
dma_kmem = kmem_cache_create ( " dma_desc " ,
sizeof ( struct s3c2410_dma_buf ) , 0 ,
2007-02-11 18:31:01 +01:00
SLAB_HWCACHE_ALIGN ,
2007-07-20 10:11:58 +09:00
s3c2410_dma_cache_ctor ) ;
2007-02-11 18:31:01 +01:00
if ( dma_kmem = = NULL ) {
printk ( KERN_ERR " dma failed to make kmem cache \n " ) ;
ret = - ENOMEM ;
goto err ;
}
2007-02-17 15:37:14 +01:00
for ( channel = 0 ; channel < channels ; channel + + ) {
2007-02-11 18:31:01 +01:00
cp = & s3c2410_chans [ channel ] ;
memset ( cp , 0 , sizeof ( struct s3c2410_dma_chan ) ) ;
/* dma channel irqs are in order.. */
cp - > number = channel ;
2007-02-17 15:37:14 +01:00
cp - > irq = channel + irq ;
cp - > regs = dma_base + ( channel * stride ) ;
2007-02-11 18:31:01 +01:00
/* point current stats somewhere */
cp - > stats = & cp - > stats_store ;
cp - > stats_store . timeout_shortest = LONG_MAX ;
/* basic channel configuration */
cp - > load_timeout = 1 < < 18 ;
printk ( " DMA channel %d at %p, irq %d \n " ,
cp - > number , cp - > regs , cp - > irq ) ;
}
return 0 ;
err :
kmem_cache_destroy ( dma_kmem ) ;
iounmap ( dma_base ) ;
dma_base = NULL ;
return ret ;
}
2007-09-04 17:19:33 +01:00
int __init s3c2410_dma_init ( void )
2007-02-17 15:37:14 +01:00
{
return s3c24xx_dma_init ( 4 , IRQ_DMA0 , 0x40 ) ;
}
2007-02-11 18:31:01 +01:00
static inline int is_channel_valid ( unsigned int channel )
{
return ( channel & DMA_CH_VALID ) ;
}
2007-02-13 13:02:52 +01:00
static struct s3c24xx_dma_order * dma_order ;
2007-02-11 18:31:01 +01:00
/* s3c2410_dma_map_channel()
*
* turn the virtual channel number into a real , and un - used hardware
* channel .
*
2007-02-13 13:02:52 +01:00
* first , try the dma ordering given to us by either the relevant
* dma code , or the board . Then just find the first usable free
* channel
2007-02-11 18:31:01 +01:00
*/
2007-04-20 11:39:46 +01:00
static struct s3c2410_dma_chan * s3c2410_dma_map_channel ( int channel )
2007-02-11 18:31:01 +01:00
{
2007-02-13 13:02:52 +01:00
struct s3c24xx_dma_order_ch * ord = NULL ;
2007-02-11 18:31:01 +01:00
struct s3c24xx_dma_map * ch_map ;
struct s3c2410_dma_chan * dmach ;
int ch ;
if ( dma_sel . map = = NULL | | channel > dma_sel . map_size )
return NULL ;
ch_map = dma_sel . map + channel ;
2007-02-13 13:02:52 +01:00
/* first, try the board mapping */
if ( dma_order ) {
ord = & dma_order - > channels [ channel ] ;
2007-02-17 15:37:14 +01:00
for ( ch = 0 ; ch < dma_channels ; ch + + ) {
2009-12-08 23:42:41 +08:00
int tmp ;
2007-02-13 13:02:52 +01:00
if ( ! is_channel_valid ( ord - > list [ ch ] ) )
continue ;
2009-12-08 23:42:41 +08:00
tmp = ord - > list [ ch ] & ~ DMA_CH_VALID ;
if ( s3c2410_chans [ tmp ] . in_use = = 0 ) {
ch = tmp ;
2007-02-13 13:02:52 +01:00
goto found ;
}
}
if ( ord - > flags & DMA_CH_NEVER )
return NULL ;
}
/* second, search the channel map for first free */
2007-02-17 15:37:14 +01:00
for ( ch = 0 ; ch < dma_channels ; ch + + ) {
2007-02-11 18:31:01 +01:00
if ( ! is_channel_valid ( ch_map - > channels [ ch ] ) )
continue ;
if ( s3c2410_chans [ ch ] . in_use = = 0 ) {
printk ( " mapped channel %d to %d \n " , channel , ch ) ;
break ;
}
}
2007-02-17 15:37:14 +01:00
if ( ch > = dma_channels )
2007-02-11 18:31:01 +01:00
return NULL ;
/* update our channel mapping */
2007-02-13 13:02:52 +01:00
found :
2007-02-11 18:31:01 +01:00
dmach = & s3c2410_chans [ ch ] ;
2008-01-28 13:01:21 +01:00
dmach - > map = ch_map ;
2009-03-19 15:02:34 +00:00
dmach - > req_ch = channel ;
2009-03-19 15:02:39 +00:00
s3c_dma_chan_map [ channel ] = dmach ;
2007-02-11 18:31:01 +01:00
/* select the channel */
( dma_sel . select ) ( dmach , ch_map ) ;
return dmach ;
}
static int s3c24xx_dma_check_entry ( struct s3c24xx_dma_map * map , int ch )
{
return 0 ;
}
int __init s3c24xx_dma_init_map ( struct s3c24xx_dma_selection * sel )
{
struct s3c24xx_dma_map * nmap ;
size_t map_sz = sizeof ( * nmap ) * sel - > map_size ;
int ptr ;
2011-12-08 10:05:52 +09:00
nmap = kmemdup ( sel - > map , map_sz , GFP_KERNEL ) ;
2007-02-11 18:31:01 +01:00
if ( nmap = = NULL )
return - ENOMEM ;
memcpy ( & dma_sel , sel , sizeof ( * sel ) ) ;
dma_sel . map = nmap ;
for ( ptr = 0 ; ptr < sel - > map_size ; ptr + + )
s3c24xx_dma_check_entry ( nmap + ptr , ptr ) ;
return 0 ;
}
2007-02-13 13:02:52 +01:00
int __init s3c24xx_dma_order_set ( struct s3c24xx_dma_order * ord )
{
struct s3c24xx_dma_order * nord = dma_order ;
if ( nord = = NULL )
nord = kmalloc ( sizeof ( struct s3c24xx_dma_order ) , GFP_KERNEL ) ;
if ( nord = = NULL ) {
printk ( KERN_ERR " no memory to store dma channel order \n " ) ;
return - ENOMEM ;
}
dma_order = nord ;
memcpy ( nord , ord , sizeof ( struct s3c24xx_dma_order ) ) ;
return 0 ;
}