来自: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通道。