linux 创建dma源地址,Linux驱动修炼之道-DMA框架源码分析(上)

努力成为linux kernel

hacker的人李万鹏原创作品,为梦而战。转载请标明出处

首先介绍一下DMA,S3C2440A支持位于系统总线和外围总线之间的4通道DMA控制器,每个通道都可以在系统总线或外围总线上的设备之间传输数据。每个通道可以对下面4种情况进行传输:

1.源和目的都在系统总线上

2.源在系统总线而目的在外围总线

3.源在外围总线而目的在系统总线

4.源和目的都在外围总线

下图是请求源为硬件模式时的每个通道的请求源:

a4c26d1e5885305701be709a3d33442f.png

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时序:

a4c26d1e5885305701be709a3d33442f.png

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,

},

};

更多内容请看:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值