s3c2410的dma操作的一般步骤

s3c2410的dma操作的一般步骤

s3c2410的dma操作的一般步骤

一般的,在s3c2440中,要想进行dma传输,需要一下七个步骤:

一:
/*  s3c2410_request_dma
 *
 * get control of an dma channel
*/
int  s3c2410_dma_request(unsigned  int  channel,
                        
struct  s3c2410_dma_client  * client,
                        
void   * dev)
    
s3c2410_dma_client的定义为:
struct  s3c2410_dma_client {
    
char      * name;
};

二:
/*  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)
 * dcon:         base value of the DCONx register
*/

int  s3c2410_dma_config(dmach_t channel,
                       
int  xferunit,
                       
int  dcon)
根据xferunit以及dcon设置通道的控制寄存器DCONx
xferunit为每次传输的数据大小:
0 : byte   1 :half word  2 :word

每个 DMA 通道都有 
9  个控制寄存器( 4  个通道 DMA 控制器共计  36  个寄存器)。
6  个寄存器用来控制 DMA 传输,其他三个监视 DMA 控制器的状态。这些寄存器的详细情况如下:
1 )DMA 初始源寄存器(DISRC)
2 )DMA初始源控制寄存器(DISRCC)
3 )DMA初始目的寄存器(DIDST)
4 )DMA初始目的控制寄存器(DIDSTC)
5 )DMA控制寄存器(DCON)
6 )DMA状态寄存器(DSTAT)
7 )DMA当前源寄存器(DCSRC)
8 )DMA当前目的寄存器(DCDST)
9 )DMA屏蔽触发寄存器(DMASKTRIG)

三:
int  s3c2410_dma_set_buffdone_fn(dmach_t channel, s3c2410_dma_cbfn_t rtn)
{
    
struct  s3c2410_dma_chan  * chan  =  lookup_dma_channel(channel);
    
if  (chan  ==  NULL)
        
return   - EINVAL;
    pr_debug(
" %s: chan=%p, callback rtn=%p\n " , __func__, chan, rtn);
    chan
-> callback_fn  =  rtn;
    
return   0 ;
}    
设置相应的dma通道完成一次dma传输后的回调函数,也即是s3c2410_dma_enqueue完成后会调用的函数

回调函数应具有一下格式:
/*  s3c2410_dma_cbfn_t
 *
 * buffer callback routine type
*/
typedef 
void  ( * s3c2410_dma_cbfn_t)( struct  s3c2410_dma_chan  * ,
                                   
void   * buf,  int  size,
                                   
enum  s3c2410_dma_buffresult result);

buf可以传递一些有用的数据,在uda1314的驱动中,传递的是audio_buf_t结构体


四:
int  s3c2410_dma_setflags(dmach_t channel, unsigned  int  flags)
{
    
struct  s3c2410_dma_chan  * chan  =  lookup_dma_channel(channel);
    
if  (chan  ==  NULL)
        
return   - EINVAL;
    pr_debug(
" %s: chan=%p, flags=%08x\n " , __func__, chan, flags);
    chan
-> flags  =  flags;
    
return   0 ;
}

五:
/*  s3c2410_dma_devconfig
 *
 * configure the dma source/destination hardware type and address
 *
 * source:    S3C2410_DMASRC_HW: source is hardware
 *            S3C2410_DMASRC_MEM: source is memory
 *
 * hwcfg:     the value for xxxSTCn register,
 *            bit 0: 0=increment pointer, 1=leave pointer
 *            bit 1: 0=source is AHB, 1=source is APB
 *
 * devaddr:   physical address of the source
*/

int  s3c2410_dma_devconfig( int  channel,
                          
enum  s3c2410_dmasrc source,
                          
int  hwcfg,
                          unsigned 
long  devaddr)
    
六:
/*
 * Allocate memory for a coherent(连续的) mapping.
 
*/
void   * dma_alloc_coherent( struct  device  * dev,size_t size,dma_addr_t  * dma_handle, int  flag)
如:        
void   * pLogicAddr  =  NULL; /* 逻辑地址 */
dma_addr_t pBusAddr 
=  NULL; /* 总线地址(实际物理地址) */
pLogicAddr 
=  dma_alloc_coherent(NULL,  480 * 640 & pBusAddr, GFP_KERNEL  |  GFP_DMA);
        
该函数实际获得两个地址,
1 、函数的返回值是一个void  * ,代表缓冲区的内核虚拟地址
2 、相关的总线地址,保存在dma_handle中
    
dma关心的是总线地址。
物理地址与总线地址
1 ) 物理地址是与CPU相关的。在CPU的地址信号线上产生的就是物理地址。在程序指令中的虚拟地址经过段映射和页面映射后,就生成了物理地址,这个物理地址被放到CPU的地址线上。
2 ) 总线地址,顾名思义,是与总线相关的,就是总线的地址线或在地址周期上产生的信号。外设使用的是总线地址。
3 ) 物理地址与总线地址之间的关系由系统的设计决定的。在x86平台上,物理地址与PCI总线地址是相同的。在其他平台上,也许会有某种转换,通常是线性的转换。    


七:
/*  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.
*/

int  s3c2410_dma_enqueue(unsigned  int  channel,  void   * id,dma_addr_t data,  int  size)
将dma_alloc_coherent中得到的dmaphys传递给s3c2410_dma_enqueue. 
s3c2410_dma_enqueue提交一次dma请求,当dma通道可用的时候通过s3c2410_dma_loadbuffer开始一次传输,传输完成后会产生irq中断。其dma的中断服务函数中会继续启动dma请求队列中的请求,传输剩下的数据。

/*  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  (chan -> callback_fn  !=  NULL) {
        (chan
-> callback_fn)(chan, buf -> id, buf -> size, result);
    }
}    
其中 s3c2410_dma_irq 会调用 s3c2410_dma_buffdone;
static  irqreturn_t s3c2410_dma_irq( int  irq,  void   * devpw)
    
/*  s3c2410_request_dma
 *
 * get control of an dma channel
*/
int  s3c2410_dma_request(unsigned  int  channel,
                        
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 " ,
             channel, __func__, chan
-> irq);

        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  */
    pr_debug(
" %s: channel initialised, %p\n " , __func__, chan);
    
return  chan -> number  |  DMACH_LOW_LEVEL;
}    


/* *
 *    request_irq - allocate an interrupt line
 *    @irq: Interrupt line to allocate
 *    @handler: Function to be called when the IRQ occurs
 *    @irqflags: Interrupt type flags
 *    @devname: An ascii name for the claiming device
 *    @dev_id: A cookie passed back to the handler function
 *
 *    This call allocates interrupt resources and enables the
 *    interrupt line and IRQ handling. From the point this
 *    call is made your handler function may be invoked. Since
 *    your handler function must clear any interrupt the board
 *    raises, you must take care both to initialise your hardware
 *    and to set up the interrupt handler in the right order.
 *
 *    Dev_id must be globally unique. Normally the address of the
 *    device data structure is used as the cookie. Since the handler
 *    receives this value it makes sense to use it.
 *
 *    If your interrupt is shared you must pass a non NULL dev_id
 *    as this is required when freeing the interrupt.
 *
 *    Flags:
 *
 *    IRQF_SHARED        Interrupt is shared
 *    IRQF_DISABLED    Disable local interrupts while processing
 *    IRQF_SAMPLE_RANDOM    The interrupt can be used for entropy
 *
 
*/
int  request_irq(unsigned  int  irq, irq_handler_t handler,
        unsigned 
long  irqflags,  const   char   * devname,  void   * dev_id)
{
    
struct  irqaction  * action;
    
int  retval;
    action 
=  kmalloc( sizeof ( struct  irqaction), GFP_ATOMIC);
    
if  ( ! action)
        
return   - ENOMEM;
    action
-> handler  =  handler;
    action
-> flags  =  irqflags;
    cpus_clear(action
-> mask);
    action
-> name  =  devname;
    action
-> next  =  NULL;
    action
-> dev_id  =  dev_id;

    select_smp_affinity(irq);

    retval 
=  setup_irq(irq, action);
    
if  (retval)
        kfree(action);

    
return  retval;
}    


/*  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;
    pr_debug(
" s3c2410_chan_loadbuffer: loading buff %p (0x%08lx,0x%06x)\n " ,
         buf, (unsigned 
long )buf -> data, buf -> size);
    
if  (buf  ==  NULL) {
        dmawarn(
" buffer is NULL\n " );
        
return   - EINVAL;
    }
    
/*  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 ;
}
 
============================================
一些相关函数的说明。供自己日后参考。
--------------------------------------------------------------------------------------
int  __init s3c24xx_dma_init_map( struct  s3c24xx_dma_selection  * sel)
初始化全局变量dma_sel结构,在此函数中大家可能疑惑的是
    nmap 
=  kmalloc(map_sz, GFP_KERNEL);
    
if  (nmap  ==  NULL)
        
return   - ENOMEM;

    memcpy(nmap, sel
-> map, map_sz);
    memcpy(
& dma_sel, sel,  sizeof ( * sel));

    dma_sel.map 
=  nmap;
这几条语句,为什么用memcpy初始化为了dma_sel,而又要用再分配的nmap结构去给dma_sel赋值。其答案是为了保险,因为dma_sel 中的map成员是个指针。对指针的操作是要很小心的。这里在此函数中重新定义一个指针变量,并指向它,就是为了防止别处定义的指针被撤消而造成越界访问。其实我认为这没有必要的。

dma_sel的定义为struct s3c24xx_dma_selection dma_sel;
而struct s3c24xx_dma_selection的定义为
struct  s3c24xx_dma_selection {
    
struct  s3c24xx_dma_map     * map;
    unsigned 
long          map_size;
    unsigned 
long          dcon_mask;

    
void     ( * select)( struct  s3c2410_dma_chan  * chan,
              
struct  s3c24xx_dma_map  * map);
};


--------------------------------------------------------------------------------------------------------
struct  s3c2410_dma_chan  * s3c2410_dma_map_channel( int  channel)
此函数功能是: turn the 
virtual  channel number into a real, and un - used hardware channel.完成DMA通道的虚实映射
在s3c2440_dma_mappings数组中查找相应channel对应的通道(
0 -- 4 ,有效未使用),并将映射关系记入dma_chan_map[channel]数组中。


---------------------------------------------------------------------------------------------------------
static   int  __init s3c2410_init_dma( void )
用于初始化s3c2410_dma_chan结构,并完成地址映射。

-----------------------------------------------------------------------------------------------------------
static   struct  s3c2410_dma_chan  * lookup_dma_channel(unsigned  int  channel)
DMA通道虚实转换

-----------------------------------------------------------------------------------------------------------
int  s3c2410_dma_request(unsigned  int  channel,
            
struct  s3c2410_dma_client  * client,
            
void   * dev)
通过s3c2410_dma_map_channel函数找到channel对应的通道,并设置相应的通道状态,申请中断
-----------------------------------------------------------------------------------------------------------

int  s3c2410_dma_started( struct  s3c2410_dma_chan  * 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 
*/
-----------------------------------------------------------------------------------------------------------
int  s3c2410_dma_free(dmach_t channel,  struct  s3c2410_dma_client  * client)
/*  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.
*/

--------------------------------------------------------------------------------------------------------------
static   int  s3c2410_dma_flush( struct  s3c2410_dma_chan  * chan)
 
*  stop the channel, and remove all current and pending transfers

--------------------------------------------------------------------------------------------------------------
int  s3c2410_dma_config(dmach_t channel,
               
int  xferunit,
               
int  dcon)
根据xferunit设置通道的控制寄存器
--------------------------------------------------------------------------------------------------------------
int  s3c2410_dma_setflags(dmach_t channel, unsigned  int  flags)
根据flags设置通道的flags
--------------------------------------------------------------------------------------------------------------


int  s3c2410_dma_ctrl(dmach_t channel,  enum  s3c2410_chan_op op);


0

你可能感兴趣的:(s3c2410的dma操作的一般步骤)