努力成为linux kernel
hacker的人李万鹏原创作品,为梦而战。转载请标明出处
首先介绍一下DMA,S3C2440A支持位于系统总线和外围总线之间的4通道DMA控制器,每个通道都可以在系统总线或外围总线上的设备之间传输数据。每个通道可以对下面4种情况进行传输:
1.源和目的都在系统总线上
2.源在系统总线而目的在外围总线
3.源在外围总线而目的在系统总线
4.源和目的都在外围总线
下图是请求源为硬件模式时的每个通道的请求源:
DMA使用3个状态的有限状态机:
1.初始状态,DMA等待DMA请求,一旦请求到达DMA进入状态2,DMA ACK与INT REQ为0。
2.在这个状态,DMA ACK置为1并且计数器CURR_TC的值被从DCON[19:0]载入,注意DMA
ACK保持为1直到它被清除。
3.在这个状态,处理DMA原子操作的子状态机被初始化。子状态机从源地址读取数据,然后写入目的地址。在这个操作中,要考虑数据的大小和传输的大小(单个/突发),这个操作重复执行直到计数器(CURR_TC)变成0在全服务模式,而只执行一次在单服务模式。当子状态机结束每一次原子操作的时候主状态机减少CURR_TC的值。另外,主状态机发出INT
REQ信号当CURR_TC变成0并且DCON[29]位被置位1时。并且,清除DMA ACK,如果下面两个条件之一满足的话:
1)在全服务模式下CURR_TC变成0
2)在单服务模式下结束原子操作
注意在单服务模式下,主有限状态机的3个状态被执行然后停止,等待另一个DMA REQ。如果DMA
REQ到来,所有的3个状态被重复执行。所以在每次原子操作中DMA
ACK被置位,然后又被清除。与之对比,在全服务模式,主有限状态机等在状态3直到CURR_TC变成0。所以,DMA
ACK在传输期间被置位,到TC为0时被清除。
然而,不管哪个服务模式,INT REQ被发出只有当CURR_TC变成0时。
如下图,是基本的DMA时序:
nXDREQ生效后等待至少2个时钟周期,nXDACK响应并开始生效,但要知道延时至少3个时钟周期,DMA控制器才可获得总线的控制权,进行读写操作一次。
下面来分析内核DMA驱动源码:
首先来看一下DMA驱动是怎样注册的:
这里使用了系统设备的概念,通过内核中源码的注释我们看看什么是系统设备。系统设备与驱动模型有一点不同,他们不需要动态的驱动绑定,也不能被探测,并且不属于任何类型的外围总线。对系统设备我们仍然有驱动的概念,因为我们仍想执行在这些设备上执行基本的操作。
在arch/arm/plat-s3c24xx/s3c244x.c中,注册了系统设备的类:
structsysdev_class s3c2440_sysclass = {
.name ="s3c2440-core",
.suspend = s3c244x_suspend,
.resume = s3c244x_resume
};
staticint__init s3c2440_core_init(void)
{
returnsysdev_class_register(&s3c2440_sysclass);
}
struct sysdev_class s3c2440_sysclass = {
.name = "s3c2440-core",
.suspend = s3c244x_suspend,
.resume = s3c244x_resume
};
static int __init s3c2440_core_init(void)
{
return sysdev_class_register(&s3c2440_sysclass);
}
在arch/arm/mach-s3c2410/dma.c中,注册了dma的驱动:
#if defined(CONFIG_CPU_S3C2410)
staticstructsysdev_driver s3c2410_dma_driver = {
.add = s3c2410_dma_add,
};
#if defined(CONFIG_CPU_S3C2410)
static struct sysdev_driver s3c2410_dma_driver = {
.add = s3c2410_dma_add,
};
把dma驱动注册到设备类下:
staticint__init s3c2410_dma_drvinit(void)
{
returnsysdev_driver_register(&s3c2410_sysclass, &s3c2410_dma_driver);
}
static int __init s3c2410_dma_drvinit(void)
{
return sysdev_driver_register(&s3c2410_sysclass, &s3c2410_dma_driver);
}
先来看一下系统设备类:
structsysdev_class {
constchar*name;
structlist_head drivers;
int(*shutdown)(structsys_device *);
int(*suspend)(structsys_device *, pm_message_t state);
int(*resume)(structsys_device *);
structkset kset;
};
struct sysdev_class {
const char *name;
struct list_head drivers;
int (*shutdown)(struct sys_device *);
int (*suspend)(struct sys_device *, pm_message_t state);
int (*resume)(struct sys_device *);
struct kset kset;
};
这个结构体有一个drivers双向循环链表,注册到这个类的驱动都挂在这里。
下面分析一下dma驱动是怎样注册的:
intsysdev_driver_register(structsysdev_class *cls,structsysdev_driver *drv)
{
。。。。。。。。。。。
if(cls && kset_get(&cls->kset)) {
list_add_tail(&drv->entry, &cls->drivers);
if(drv->add) {
structsys_device *dev;
list_for_each_entry(dev, &cls->kset.list, kobj.entry)
drv->add(dev);
}
}else{
err = -EINVAL;
WARN(1, KERN_ERR"%s: invalid device class\n", __func__);
}
mutex_unlock(&sysdev_drivers_lock);
returnerr;
}
int sysdev_driver_register(struct sysdev_class *cls, struct sysdev_driver *drv)
{
。。。。。。。。。。。
if (cls && kset_get(&cls->kset)) {
list_add_tail(&drv->entry, &cls->drivers);
if (drv->add) {
struct sys_device *dev;
list_for_each_entry(dev, &cls->kset.list, kobj.entry)
drv->add(dev);
}
} else {
err = -EINVAL;
WARN(1, KERN_ERR "%s: invalid device class\n", __func__);
}
mutex_unlock(&sysdev_drivers_lock);
return err;
}
在arch/arm/mach-s3c2440/s3c2440.c中,注册了一个系统设备s3c2440_sysdev,
staticstructsys_device s3c2440_sysdev = {
.cls = &s3c2440_sysclass,
};
int__init s3c2440_init(void)
{
。。。。。。。。
returnsysdev_register(&s3c2440_sysdev);
}
static struct sys_device s3c2440_sysdev = {
.cls = &s3c2440_sysclass,
};
int __init s3c2440_init(void)
{
。。。。。。。。
return sysdev_register(&s3c2440_sysdev);
}
注意系统设备这个结构体,里边封装了一个系统设备类。
structsys_device {
u32 id;
structsysdev_class * cls;
structkobject kobj;
};
struct sys_device {
u32 id;
struct sysdev_class * cls;
struct kobject kobj;
};
下面来看一下系统设备的注册,系统设备是一个虚拟设备,这里的目的就是为了调用driver的add函数。
intsysdev_register(structsys_device *sysdev){
。。。。。。。。。。。。
list_for_each_entry(drv, &cls->drivers, entry) {
if(drv->add)
drv->add(sysdev);
}
。。。。。。。。。。。。。
}
int sysdev_register(struct sys_device *sysdev){
。。。。。。。。。。。。
list_for_each_entry(drv, &cls->drivers, entry) {
if (drv->add)
drv->add(sysdev);
}
。。。。。。。。。。。。。
}
下面来分析一下这个add函数。看上边的那个dma驱动的结构体,指明了add函数为s3c2410_dma_add:
staticint__init s3c2410_dma_add(structsys_device *sysdev)
{
s3c2410_dma_init(); (一)
s3c24xx_dma_order_set(&s3c2410_dma_order); (二)
returns3c24xx_dma_init_map(&s3c2410_dma_sel); (三)
}
static int __init s3c2410_dma_add(struct sys_device *sysdev)
{
s3c2410_dma_init(); (一)
s3c24xx_dma_order_set(&s3c2410_dma_order); (二)
return s3c24xx_dma_init_map(&s3c2410_dma_sel); (三)
}
分别对s3c2410_dma_add中的3个函数进行分析:
(一)
int__init s3c2410_dma_init(void)
{
returns3c24xx_dma_init(4, IRQ_DMA0, 0x40);
}
int __init s3c2410_dma_init(void)
{
return s3c24xx_dma_init(4, IRQ_DMA0, 0x40);
}
int__init s3c24xx_dma_init(unsignedintchannels, unsignedintirq,
unsignedintstride)
{
structs3c2410_dma_chan *cp;
intchannel;
intret;
printk("S3C24XX DMA Driver, (c) 2003-2004,2006 Simtec Electronics\n");
dma_channels = channels;
dma_base = ioremap(S3C24XX_PA_DMA, stride * channels);
if(dma_base == NULL) {
printk(KERN_ERR"dma failed to remap register block\n");
return-ENOMEM;
}
dma_kmem = kmem_cache_create("dma_desc",
sizeof(structs3c2410_dma_buf), 0,
SLAB_HWCACHE_ALIGN,
s3c2410_dma_cache_ctor);
if(dma_kmem == NULL) {
printk(KERN_ERR"dma failed to make kmem cache\n");
ret = -ENOMEM;
gotoerr;
}
for(channel = 0; channel
cp = &s3c2410_chans[channel];
memset(cp, 0,sizeof(structs3c2410_dma_chan));
cp->number = channel;//通道号
cp->irq = channel + irq;//通道中断号
cp->regs = dma_base + (channel * stride);//通道寄存器基址
cp->stats = &cp->stats_store;
cp->stats_store.timeout_shortest = LONG_MAX;
cp->load_timeout = 1<<18;
printk("DMA channel %d at %p, irq %d\n",
cp->number, cp->regs, cp->irq);
}
return0;
err:
kmem_cache_destroy(dma_kmem);
iounmap(dma_base);
dma_base = NULL;
returnret;
}
int __init s3c24xx_dma_init(unsigned int channels, unsigned int irq,
unsigned int stride)
{
struct s3c2410_dma_chan *cp;
int channel;
int ret;
printk("S3C24XX DMA Driver, (c) 2003-2004,2006 Simtec Electronics\n");
dma_channels = channels;
dma_base = ioremap(S3C24XX_PA_DMA, stride * channels);
if (dma_base == NULL) {
printk(KERN_ERR "dma failed to remap register block\n");
return -ENOMEM;
}
dma_kmem = kmem_cache_create("dma_desc",
sizeof(struct s3c2410_dma_buf), 0,
SLAB_HWCACHE_ALIGN,
s3c2410_dma_cache_ctor);
if (dma_kmem == NULL) {
printk(KERN_ERR "dma failed to make kmem cache\n");
ret = -ENOMEM;
goto err;
}
for (channel = 0; channel < channels; channel++) {
cp = &s3c2410_chans[channel];
memset(cp, 0, sizeof(struct s3c2410_dma_chan));
cp->number = channel; //通道号
cp->irq = channel + irq; //通道中断号
cp->regs = dma_base + (channel * stride); //通道寄存器基址
cp->stats = &cp->stats_store;
cp->stats_store.timeout_shortest = LONG_MAX;
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;
}
这里使用到了一个s3c2410_dma_chan结构体,struct
s3c2410_dma_chan记录dma通道信息,内容如下:
151structs3c2410_dma_chan {
152
153 unsignedcharnumber;//dma通道号,
154 unsignedcharin_use;//当前通道是否已经使用
155 unsignedcharirq_claimed;// 有无dma中断
156 unsignedcharirq_enabled;//是否使能了dma中断
157 unsignedcharxfer_unit;//传输块大小
158
159
160
161enums3c2410_dma_state state;
162enums3c2410_dma_loadst load_state;
163structs3c2410_dma_client *client;
164
165
166enums3c2410_dmasrc source;
167enumdma_ch req_ch;
168 unsignedlongdev_addr;
169 unsignedlongload_timeout;
170 unsignedintflags;
171
172structs3c24xx_dma_map *map;
173
174
175void__iomem *regs;
176void__iomem *addr_reg;
177 unsignedintirq; 中断号
178 unsignedlongdcon; /默认控制寄存器的值
179
180
181 s3c2410_dma_cbfn_t callback_fn; 传输完成回调函数
182 s3c2410_dma_opfn_t op_fn; 操作完成回调函数*/
183
184
185structs3c2410_dma_stats *stats;
186structs3c2410_dma_stats stats_store;
187
188
189structs3c2410_dma_buf *curr;
190structs3c2410_dma_buf *next;
191structs3c2410_dma_buf *end;dma缓冲区链表
192
193
194structsys_device dev;
195 };
151 struct s3c2410_dma_chan {
152
153 unsigned char number; //dma通道号,
154 unsigned char in_use; //当前通道是否已经使用
155 unsigned char irq_claimed; // 有无dma中断
156 unsigned char irq_enabled; //是否使能了dma中断
157 unsigned char xfer_unit; //传输块大小
158
159
160
161 enum s3c2410_dma_state state;
162 enum s3c2410_dma_loadst load_state;
163 struct s3c2410_dma_client *client;
164
165
166 enum s3c2410_dmasrc source;
167 enum dma_ch req_ch;
168 unsigned long dev_addr;
169 unsigned long load_timeout;
170 unsigned int flags;
171
172 struct s3c24xx_dma_map *map;
173
174
175 void __iomem *regs;
176 void __iomem *addr_reg;
177 unsigned int irq; 中断号
178 unsigned long dcon; /默认控制寄存器的值
179
180
181 s3c2410_dma_cbfn_t callback_fn; 传输完成回调函数
182 s3c2410_dma_opfn_t op_fn; 操作完成回调函数*/
183
184
185 struct s3c2410_dma_stats *stats;
186 struct s3c2410_dma_stats stats_store;
187
188
189 struct s3c2410_dma_buf *curr;
190 struct s3c2410_dma_buf *next;
191 struct s3c2410_dma_buf *end; dma缓冲区链表
192
193
194 struct sys_device dev;
195 };
(二)
先看下边一个结构体,s3c2410_dma_order。这个是建立目标板dma源与硬件的dma通道的关联。
staticstructs3c24xx_dma_order __initdata s3c2410_dma_order = {
.channels = {
[DMACH_SDI] = {
.list = {
[0] = 3 | DMA_CH_VALID,
[1] = 2 | DMA_CH_VALID,
[2] = 0 | DMA_CH_VALID,
},
},
[DMACH_I2S_IN] = {
.list = {
[0] = 1 | DMA_CH_VALID,
[1] = 2 | DMA_CH_VALID,
},
},
},
};
static struct s3c24xx_dma_order __initdata s3c2410_dma_order = {
.channels = {
[DMACH_SDI] = {
.list = {
[0] = 3 | DMA_CH_VALID,
[1] = 2 | DMA_CH_VALID,
[2] = 0 | DMA_CH_VALID,
},
},
[DMACH_I2S_IN] = {
.list = {
[0] = 1 | DMA_CH_VALID,
[1] = 2 | DMA_CH_VALID,
},
},
},
};
分析这里SDI可以是使用通道3,2,0,为什么从大到小排列,是因为某些dma请求只能使用dma0,dma1等较小的通道号,比如外部总线dma只能只用dma0,为了避免sdi占用,这里就采用的这种排列。
[DMACH_SDI] = {
.list = {
[0] = 3 | DMA_CH_VALID,
[1] = 2 | DMA_CH_VALID,
[2] = 0 | DMA_CH_VALID,
},
},
[DMACH_SDI] = {
.list = {
[0] = 3 | DMA_CH_VALID,
[1] = 2 | DMA_CH_VALID,
[2] = 0 | DMA_CH_VALID,
},
},
注意这个结构体是用__initdata修饰的,所以在初始化后会被释放掉。下边这个函数重新分配内存保存这个结构体就是这个原因。
int__init s3c24xx_dma_order_set(structs3c24xx_dma_order *ord)
{
structs3c24xx_dma_order *nord = dma_order;
if(nord == NULL)
nord = kmalloc(sizeof(structs3c24xx_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(structs3c24xx_dma_order));
return0;
}
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;
}
(三)
structs3c24xx_dma_map {
constchar*name;//DMA源的名
structs3c24xx_dma_addr hw_addr;//源的物理地址
unsignedlongchannels[S3C2410_DMA_CHANNELS];//DMA通道信息
unsignedlongchannels_rx[S3C2410_DMA_CHANNELS];
};
structs3c24xx_dma_selection {
structs3c24xx_dma_map *map;//记录了struct s3c24xx_dma_map数组的首地址
unsignedlongmap_size;//struct s3c24xx_dma_map数组的成员个数
unsignedlongdcon_mask;//dma控制器掩码
void(*select)(structs3c2410_dma_chan *chan,
structs3c24xx_dma_map *map);//源选择函数
void(*direction)(structs3c2410_dma_chan *chan,//dma方向
structs3c24xx_dma_map *map,
enums3c2410_dmasrc dir);
};
struct s3c24xx_dma_map {
const char *name; //DMA源的名
struct s3c24xx_dma_addr hw_addr; //源的物理地址
unsigned long channels[S3C2410_DMA_CHANNELS]; //DMA通道信息
unsigned long channels_rx[S3C2410_DMA_CHANNELS];
};
struct s3c24xx_dma_selection {
struct s3c24xx_dma_map *map; //记录了struct s3c24xx_dma_map数组的首地址
unsigned long map_size; //struct s3c24xx_dma_map数组的成员个数
unsigned long dcon_mask; //dma控制器掩码
void (*select)(struct s3c2410_dma_chan *chan,
struct s3c24xx_dma_map *map); //源选择函数
void (*direction)(struct s3c2410_dma_chan *chan, //dma方向
struct s3c24xx_dma_map *map,
enum s3c2410_dmasrc dir);
};
建立芯片本身的dma源与硬件dma通道的视图。
int__init s3c24xx_dma_init_map(structs3c24xx_dma_selection *sel)
{
structs3c24xx_dma_map *nmap;
size_t map_sz =sizeof(*nmap) * sel->map_size;
intptr;
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;
for(ptr = 0; ptr map_size; ptr++)
s3c24xx_dma_check_entry(nmap+ptr, ptr);
return0;
}
staticstructs3c24xx_dma_selection __initdata s3c2410_dma_sel = {
.select = s3c2410_dma_select,//通道选择函数
.dcon_mask = 7 <
.map = s3c2410_dma_mappings,//dma源与硬件dma通道的视图
.map_size = ARRAY_SIZE(s3c2410_dma_mappings),//虚拟通道的数目
};
staticvoids3c2410_dma_select(structs3c2410_dma_chan *chan,
structs3c24xx_dma_map *map)
{
chan->dcon = map->channels[chan->number] & ~DMA_CH_VALID;//选择通道,并设置成请求模式
}
staticstructs3c24xx_dma_map __initdata s3c2410_dma_mappings[] = {
[DMACH_XD0] = {
.name ="xdreq0",
.channels[0] = S3C2410_DCON_CH0_XDREQ0 | DMA_CH_VALID,
},
[DMACH_XD1] = {
.name ="xdreq1",
.channels[1] = S3C2410_DCON_CH1_XDREQ1 | DMA_CH_VALID,
},
[DMACH_SDI] = {
.name ="sdi",
.channels[0] = S3C2410_DCON_CH0_SDI | DMA_CH_VALID,
.channels[2] = S3C2410_DCON_CH2_SDI | DMA_CH_VALID,
.channels[3] = S3C2410_DCON_CH3_SDI | DMA_CH_VALID,
.hw_addr.to = S3C2410_PA_IIS + S3C2410_IISFIFO,
.hw_addr.from = S3C2410_PA_IIS + S3C2410_IISFIFO,
},
[DMACH_SPI0] = {
.name ="spi0",
.channels[1] = S3C2410_DCON_CH1_SPI | DMA_CH_VALID,
.hw_addr.to = S3C2410_PA_SPI + S3C2410_SPTDAT,
.hw_addr.from = S3C2410_PA_SPI + S3C2410_SPRDAT,
},
[DMACH_SPI1] = {
.name ="spi1",
.channels[3] = S3C2410_DCON_CH3_SPI | DMA_CH_VALID,
.hw_addr.to = S3C2410_PA_SPI + 0x20 + S3C2410_SPTDAT,
.hw_addr.from = S3C2410_PA_SPI + 0x20 + S3C2410_SPRDAT,
},
[DMACH_UART0] = {
.name ="uart0",
.channels[0] = S3C2410_DCON_CH0_UART0 | DMA_CH_VALID,
.hw_addr.to = S3C2410_PA_UART0 + S3C2410_UTXH,
.hw_addr.from = S3C2410_PA_UART0 + S3C2410_URXH,
},
[DMACH_UART1] = {
.name ="uart1",
.channels[1] = S3C2410_DCON_CH1_UART1 | DMA_CH_VALID,
.hw_addr.to = S3C2410_PA_UART1 + S3C2410_UTXH,
.hw_addr.from = S3C2410_PA_UART1 + S3C2410_URXH,
},
[DMACH_UART2] = {
.name ="uart2",
.channels[3] = S3C2410_DCON_CH3_UART2 | DMA_CH_VALID,
.hw_addr.to = S3C2410_PA_UART2 + S3C2410_UTXH,
.hw_addr.from = S3C2410_PA_UART2 + S3C2410_URXH,
},
[DMACH_TIMER] = {
.name ="timer",
.channels[0] = S3C2410_DCON_CH0_TIMER | DMA_CH_VALID,
.channels[2] = S3C2410_DCON_CH2_TIMER | DMA_CH_VALID,
.channels[3] = S3C2410_DCON_CH3_TIMER | DMA_CH_VALID,
},
[DMACH_I2S_IN] = {
.name ="i2s-sdi",
.channels[1] = S3C2410_DCON_CH1_I2SSDI | DMA_CH_VALID,
.channels[2] = S3C2410_DCON_CH2_I2SSDI | DMA_CH_VALID,
.hw_addr.from = S3C2410_PA_IIS + S3C2410_IISFIFO,
},
[DMACH_I2S_OUT] = {
.name ="i2s-sdo",
.channels[2] = S3C2410_DCON_CH2_I2SSDO | DMA_CH_VALID,
.hw_addr.to = S3C2410_PA_IIS + S3C2410_IISFIFO,
},
[DMACH_USB_EP1] = {
.name ="usb-ep1",
.channels[0] = S3C2410_DCON_CH0_USBEP1 | DMA_CH_VALID,
},
[DMACH_USB_EP2] = {
.name ="usb-ep2",
.channels[1] = S3C2410_DCON_CH1_USBEP2 | DMA_CH_VALID,
},
[DMACH_USB_EP3] = {
.name ="usb-ep3",
.channels[2] = S3C2410_DCON_CH2_USBEP3 | DMA_CH_VALID,
},
[DMACH_USB_EP4] = {
.name ="usb-ep4",
.channels[3] =S3C2410_DCON_CH3_USBEP4 | DMA_CH_VALID,
},
};
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;
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;
for (ptr = 0; ptr < sel->map_size; ptr++)
s3c24xx_dma_check_entry(nmap+ptr, ptr);
return 0;
}
static struct s3c24xx_dma_selection __initdata s3c2410_dma_sel = {
.select = s3c2410_dma_select, //通道选择函数
.dcon_mask = 7 << 24, //屏蔽DMA控制寄存器中用于选择请求源的位
.map = s3c2410_dma_mappings, //dma源与硬件dma通道的视图
.map_size = ARRAY_SIZE(s3c2410_dma_mappings), //虚拟通道的数目
};
static void s3c2410_dma_select(struct s3c2410_dma_chan *chan,
struct s3c24xx_dma_map *map)
{
chan->dcon = map->channels[chan->number] & ~DMA_CH_VALID; //选择通道,并设置成请求模式
}
static struct s3c24xx_dma_map __initdata s3c2410_dma_mappings[] = {
[DMACH_XD0] = {
.name = "xdreq0",
.channels[0] = S3C2410_DCON_CH0_XDREQ0 | DMA_CH_VALID,
},
[DMACH_XD1] = {
.name = "xdreq1",
.channels[1] = S3C2410_DCON_CH1_XDREQ1 | DMA_CH_VALID,
},
[DMACH_SDI] = {
.name = "sdi",
.channels[0] = S3C2410_DCON_CH0_SDI | DMA_CH_VALID,
.channels[2] = S3C2410_DCON_CH2_SDI | DMA_CH_VALID,
.channels[3] = S3C2410_DCON_CH3_SDI | DMA_CH_VALID,
.hw_addr.to = S3C2410_PA_IIS + S3C2410_IISFIFO,
.hw_addr.from = S3C2410_PA_IIS + S3C2410_IISFIFO,
},
[DMACH_SPI0] = {
.name = "spi0",
.channels[1] = S3C2410_DCON_CH1_SPI | DMA_CH_VALID,
.hw_addr.to = S3C2410_PA_SPI + S3C2410_SPTDAT,
.hw_addr.from = S3C2410_PA_SPI + S3C2410_SPRDAT,
},
[DMACH_SPI1] = {
.name = "spi1",
.channels[3] = S3C2410_DCON_CH3_SPI | DMA_CH_VALID,
.hw_addr.to = S3C2410_PA_SPI + 0x20 + S3C2410_SPTDAT,
.hw_addr.from = S3C2410_PA_SPI + 0x20 + S3C2410_SPRDAT,
},
[DMACH_UART0] = {
.name = "uart0",
.channels[0] = S3C2410_DCON_CH0_UART0 | DMA_CH_VALID,
.hw_addr.to = S3C2410_PA_UART0 + S3C2410_UTXH,
.hw_addr.from = S3C2410_PA_UART0 + S3C2410_URXH,
},
[DMACH_UART1] = {
.name = "uart1",
.channels[1] = S3C2410_DCON_CH1_UART1 | DMA_CH_VALID,
.hw_addr.to = S3C2410_PA_UART1 + S3C2410_UTXH,
.hw_addr.from = S3C2410_PA_UART1 + S3C2410_URXH,
},
[DMACH_UART2] = {
.name = "uart2",
.channels[3] = S3C2410_DCON_CH3_UART2 | DMA_CH_VALID,
.hw_addr.to = S3C2410_PA_UART2 + S3C2410_UTXH,
.hw_addr.from = S3C2410_PA_UART2 + S3C2410_URXH,
},
[DMACH_TIMER] = {
.name = "timer",
.channels[0] = S3C2410_DCON_CH0_TIMER | DMA_CH_VALID,
.channels[2] = S3C2410_DCON_CH2_TIMER | DMA_CH_VALID,
.channels[3] = S3C2410_DCON_CH3_TIMER | DMA_CH_VALID,
},
[DMACH_I2S_IN] = {
.name = "i2s-sdi",
.channels[1] = S3C2410_DCON_CH1_I2SSDI | DMA_CH_VALID,
.channels[2] = S3C2410_DCON_CH2_I2SSDI | DMA_CH_VALID,
.hw_addr.from = S3C2410_PA_IIS + S3C2410_IISFIFO,
},
[DMACH_I2S_OUT] = {
.name = "i2s-sdo",
.channels[2] = S3C2410_DCON_CH2_I2SSDO | DMA_CH_VALID,
.hw_addr.to = S3C2410_PA_IIS + S3C2410_IISFIFO,
},
[DMACH_USB_EP1] = {
.name = "usb-ep1",
.channels[0] = S3C2410_DCON_CH0_USBEP1 | DMA_CH_VALID,
},
[DMACH_USB_EP2] = {
.name = "usb-ep2",
.channels[1] = S3C2410_DCON_CH1_USBEP2 | DMA_CH_VALID,
},
[DMACH_USB_EP3] = {
.name = "usb-ep3",
.channels[2] = S3C2410_DCON_CH2_USBEP3 | DMA_CH_VALID,
},
[DMACH_USB_EP4] = {
.name = "usb-ep4",
.channels[3] =S3C2410_DCON_CH3_USBEP4 | DMA_CH_VALID,
},
};
更多内容请看: