linux-2.6.24.4中s3c2410和dma有关的函数的分析

本文详细介绍了S3C2410 DMA控制器的寄存器配置及Linux内核中DMA资源的申请、配置过程。通过具体函数分析,展示了如何设置DMA通道、控制寄存器等关键步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

来自:http://blog.chinaunix.net/u1/57747/showart_718784.html

 

首先介绍s3c2410与DMA相关的寄存器。

s3c2410共有4通道的dma,每通道9个寄存器,共36个。
 
    1、DISRCn 该寄存器保存待传送数据的源地址。
    2、DISRCCn 源控制寄存器。位1表示数据源的总线类型,位0表示地址是否自动增减。
    3、DIDSTn 该寄存器保存待传送数据的目的地址。
    4、DIDSTCn 目的控制寄存器。位1表示目的地址的总线类型,位0表示地址是否自动增减。
    5、DCON  DMA控制寄存器。
    6、DSTATn DMA状态寄存器。
    7、DCSRCn 当前源地址寄存器。
    8、DCDSTn 当前目的地址寄存器。
    9、DMASKTRIGn DMA MASK寄存器。
 
 
下面是linux-2.6.24.4中和dma有关的函数的分析:
 
首先定义了几个变量:
static int dma_channels ;//被设定为4
static struct s3c24xx_dma_selection dma_sel ;
struct s3c2410_dma_chan s3c2410_chans [S3C2410_DMA_CHANNELS];
static struct s3c24xx_dma_order *dma_order ;

有关的结构体定义如下:

 

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 s3c24xx_dma_map {
 const char * name;
 struct s3c24xx_dma_addr hw_addr;
 unsigned long channels[ S3C2410_DMA_CHANNELS] ;
} ;

struct s3c2410_dma_chan {
 /* channel state flags and information * /
 unsigned char number; /* number of this dma channel * /
 unsigned char in_use; /* channel allocated * /
 unsigned char irq_claimed; /* irq claimed for channel * /
 unsigned char irq_enabled; /* irq enabled for channel * /
 unsigned char xfer_unit; /* size of an transfer * /
 /* channel state * /
 enum s3c2410_dma_state state;
 enum s3c2410_dma_loadst load_state;
 struct s3c2410_dma_client * client;
 /* channel configuration * /
 enum s3c2410_dmasrc source;
 unsigned long dev_addr;
 unsigned long load_timeout;
 unsigned int flags; /* channel flags * /
 struct s3c24xx_dma_map * map; /* channel hw maps * /
 /* channel

struct s3c24xx_dma_order {
 struct s3c24xx_dma_order_ch channels[ DMACH_MAX] ;
} ;

struct s3c24xx_dma_order_ch {
 unsigned int list [ S3C2410_DMA_CHANNELS] ; /* list of channels */
 unsigned int flags; /* flags */
} ;

 

然后分析各个函数。

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) ; //获得dma通道

 if ( chan = = NULL ) {
  local_irq_restore( flags) ;
  return - EBUSY;
 }
 dbg_showchan( chan) ;
 chan- > client = client;
 chan- > in_use = 1; //占用该dma通道

 if ( ! chan- > irq_claimed) {
  pr_debug( "dma%d: %s : requesting irq %d/n" ,
    channel, __FUNCTION__ , 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" , __FUNCTION__ , chan) ;
 return 0;
}

 
可见,该函数向内核申请了资源,即dma通道,然后就可以对申请的通道进行设置了。下面继续。
 

int s3c2410_dma_config( dmach_t channel, //通道号,和申请时使用的通道号要一致

         int xferunit, //发送时位的设置,可以是1、2、或者4,分别对应8、16和32位方式

         int dcon) //特别重要,就是dcon寄存器的值

{
 struct s3c2410_dma_chan * chan = lookup_dma_channel( channel) ; //得到申请的dma通道

 pr_debug( "%s: chan=%d, xfer_unit=%d, dcon=%08x/n" ,
   __FUNCTION__ , channel, xferunit, dcon) ;
 if ( chan = = NULL )
  return - EINVAL;
 pr_debug( "%s: Initial dcon is %08x/n" , __FUNCTION__ , dcon) ;
 dcon | = chan- > dcon & dma_sel. dcon_mask; //这步的作用是清除dcon寄存器中除了24~26的其他位,从datasheet中知道这三位

      //决定dma的中断源

 pr_debug( "%s: New dcon is %08x/n" , __FUNCTION__ , dcon) ;
 switch ( xferunit) { //设置字节传送的方式

 case 1:
  dcon | = S3C2410_DCON_BYTE;
  break ;
 case 2:
  dcon | = S3C2410_DCON_HALFWORD;
  break ;
 case 4:
  dcon | = S3C2410_DCON_WORD;
  break ;
 default :
  pr_debug( "%s: bad transfer size %d/n" , __FUNCTION__ , xferunit) ;
  return - EINVAL;
 }
 dcon | = S3C2410_DCON_HWTRIG; //使24~26位的选择有效

 dcon | = S3C2410_DCON_INTREQ; //使传送完成的时候产生中断

 pr_debug( "%s: dcon now %08x/n" , __FUNCTION__ , dcon) ;
 chan- > dcon = dcon;
 chan- > xfer_unit = xferunit;
 return 0;
}

由以上分析可以知道,该函数完成的主要功能就是设置对应通道的dcon寄存器。关于更细节的东西,可以查看datasheet。

int s3c2410_dma_devconfig( int channel,
     enum s3c2410_dmasrc source, //dma传送源的类型,可以是S3C2410_DMASRC_HW和S3C2410_DMASRC_MEM

     int hwcfg,
     unsigned long devaddr) //传送的目的地址

{
 struct s3c2410_dma_chan * chan = lookup_dma_channel( channel) ;
 if ( chan = = NULL )
  return - EINVAL;
 pr_debug( "%s: source=%d, hwcfg=%08x, devaddr=%08lx/n" ,
   __FUNCTION__ , ( int ) source, hwcfg, devaddr) ;
 chan- > source = source;
 chan- > dev_addr = devaddr;
 switch ( source) {
 case S3C2410_DMASRC_HW: //dma传送源是外围硬件

  /* source is hardware */
  pr_debug( "%s: hw source, devaddr=%08lx, hwcfg=%d/n" ,
    __FUNCTION__ , devaddr, hwcfg) ;
  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) ; //得到传送目的地址寄存器的地址

  return 0;
 case S3C2410_DMASRC_MEM: //dma传送源是内存

  /* source is memory */
  pr_debug( "%s: mem source, devaddr=%08lx, hwcfg=%d/n" ,
     __FUNCTION__ , devaddr, hwcfg) ;
  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) ; //得到传送源地址寄存器的地址

  return 0;
 }
 printk( KERN_ERR "dma%d: invalid source type (%d)/n" , channel, source) ;
 return - EINVAL;
}

 

整个系统中dma的建立过程如下:

首先调用了s3c2410_dma_init(),该函数只有一句:
return s3c24xx_dma_init(4, IRQ_DMA0, 0x40);
其中第一个参数就是通道号,第二个参数为dma的中断号的基数,第三个参数表示各个通道占用的空间的大小。
在函数s3c24xx_dma_init中,主要做了以下几件事情:
1、调用ioremap,将dma控制器的地址做一个映射。
2、为dma分配内核空间。
3、将上面提到的s3c2410_chans数组的内容全部清零。
4、初始化4个s3c2410_dma_chan结构的变量,对其中的一部分成员赋值。
成功结束时,该函数返回0。
 
 
第二步,系统调用s3c24xx_dma_order_set函数,如下:
s3c24xx_dma_order_set(&s3c2410_dma_order);
s3c2410_dma_order定义于arch/arm/mach-s3c2410/dma.c文件中,是一个s3c24xx_dma_order类型的结构体。
该函数主要为该结构体分配空间,然后将s3c2410_dma_order的内容copy到dma_order中。
 
 
第三步,系统调用s3c24xx_dma_init_map,完成dma通道的映射:
return s3c24xx_dma_init_map(&s3c2410_dma_sel);
下面重点分析一下该函数。
传给该函数的参数定义如下:
 

static struct s3c24xx_dma_selection __initdata s3c2410_dma_sel = {
 . select = s3c2410_dma_select,
 . dcon_mask = 7 < < 24,
 . map = s3c2410_dma_mappings,
 . map_size = ARRAY_SIZE( s3c2410_dma_mappings) ,
} ;

该函数首先为各个通道分配内存空间,分配的总的大小为sizeof(struct s3c24xx_dma_map ) * s3c2410_dma_sel.map_size。
然后,函数把s3c2410_dma_sel的所有内容全部赋给上面提到的dma_sel结构体,该结构体就包含了所有的dma可以申请的通道。
用户申请dma通道的时候,内核就可以在dma_sel的map表中查找,并决定是否可以使用该dma通道。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值