首先介绍一下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中,注册了系统设备的类:
- 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);
- }
- 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)
- static struct sysdev_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驱动注册到设备类下:
- static int __init s3c2410_dma_drvinit(void)
- {
- return sysdev_driver_register(&s3c2410_sysclass, &s3c2410_dma_driver);
- }
- static int __init s3c2410_dma_drvinit(void)
- {
- return sysdev_driver_register(&s3c2410_sysclass, &s3c2410_dma_driver);
- }
先来看一下系统设备类:
- struct sysdev_class {
- const char *name;
- struct list_head drivers;
- /* Default operations for these types of devices */
- int (*shutdown)(struct sys_device *);
- int (*suspend)(struct sys_device *, pm_message_t state);
- int (*resume)(struct sys_device *);
- struct kset kset;
- };
- struct sysdev_class {
- const char *name;
- struct list_head drivers;
- /* Default operations for these types of devices */
- int (*shutdown)(struct sys_device *);
- int (*suspend)(struct sys_device *, pm_message_t state);
- int (*resume)(struct sys_device *);
- struct kset kset;
- };
这个结构体有一个drivers双向循环链表,注册到这个类的驱动都挂在这里。
下面分析一下dma驱动是怎样注册的:
- 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);
- /*如果驱动定义了add方法,则为类下的每个设备调用驱动的add方法*/
- 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;
- }
- 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);
- /*如果驱动定义了add方法,则为类下的每个设备调用驱动的add方法*/
- 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,
- static struct sys_device s3c2440_sysdev = {
- .cls = &s3c2440_sysclass,
- };
- int __init s3c2440_init(void)
- {
- 。。。。。。。。
- return sysdev_register(&s3c2440_sysdev);
- }
- static struct sys_device s3c2440_sysdev = {
- .cls = &s3c2440_sysclass,
- };
- int __init s3c2440_init(void)
- {
- 。。。。。。。。
- return sysdev_register(&s3c2440_sysdev);
- }
注意系统设备这个结构体,里边封装了一个系统设备类。
- struct sys_device {
- u32 id;
- struct sysdev_class * cls;
- struct kobject kobj;
- };
- struct sys_device {
- u32 id;
- struct sysdev_class * cls;
- struct kobject kobj;
- };
下面来看一下系统设备的注册,系统设备是一个虚拟设备,这里的目的就是为了调用driver的add函数。
- int sysdev_register(struct sys_device *sysdev){
- 。。。。。。。。。。。。
- /* Notify class auxillary drivers */
- list_for_each_entry(drv, &cls->drivers, entry) {
- /*为这个设备调用了设备类下所有驱动的add函数*/
- if (drv->add)
- drv->add(sysdev);
- }
- 。。。。。。。。。。。。。
- }
- int sysdev_register(struct sys_device *sysdev){
- 。。。。。。。。。。。。
- /* Notify class auxillary drivers */
- list_for_each_entry(drv, &cls->drivers, entry) {
- /*为这个设备调用了设备类下所有驱动的add函数*/
- if (drv->add)
- drv->add(sysdev);
- }
- 。。。。。。。。。。。。。
- }
下面来分析一下这个add函数。看上边的那个dma驱动的结构体,指明了add函数为s3c2410_dma_add:
- 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); (三)
- }
- 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)
- {
- /*4个通道,中断号为IRQ_DMA0,每一个通道的寄存器覆盖的地址范围为0x40*/
- return s3c24xx_dma_init(4, IRQ_DMA0, 0x40);
- }
- int __init s3c2410_dma_init(void)
- {
- /*4个通道,中断号为IRQ_DMA0,每一个通道的寄存器覆盖的地址范围为0x40*/
- return s3c24xx_dma_init(4, IRQ_DMA0, 0x40);
- }
- int __init s3c24xx_dma_init(unsigned int channels, unsigned int irq,
- unsigned int stride)
- {
- /*每一个通道用一个s3c2410_dma_chan结构体描述*/
- struct s3c2410_dma_chan *cp;
- int channel;
- int ret;
- printk("S3C24XX DMA Driver, (c) 2003-2004,2006 Simtec Electronics/n");
- /*dma_channels是一个全局变量,用来存放通道数量*/
- dma_channels = channels;
- /*获得DMA寄存器的虚拟起始地址*/
- dma_base = ioremap(S3C24XX_PA_DMA, stride * channels);
- if (dma_base == NULL) {
- printk(KERN_ERR "dma failed to remap register block/n");
- return -ENOMEM;
- }
- /*分配一个高速缓冲区,以后用来分配s3c2410_dma_buf*/
- 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); //通道寄存器基址
- /* point current stats somewhere */
- cp->stats = &cp->stats_store;
- cp->stats_store.timeout_shortest = LONG_MAX;
- /* basic channel configuration */
- /*设置加载的超时时间*/
- 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;
- }
- int __init s3c24xx_dma_init(unsigned int channels, unsigned int irq,
- unsigned int stride)
- {
- /*每一个通道用一个s3c2410_dma_chan结构体描述*/
- struct s3c2410_dma_chan *cp;
- int channel;
- int ret;
- printk("S3C24XX DMA Driver, (c) 2003-2004,2006 Simtec Electronics/n");
- /*dma_channels是一个全局变量,用来存放通道数量*/
- dma_channels = channels;
- /*获得DMA寄存器的虚拟起始地址*/
- dma_base = ioremap(S3C24XX_PA_DMA, stride * channels);
- if (dma_base == NULL) {
- printk(KERN_ERR "dma failed to remap register block/n");
- return -ENOMEM;
- }
- /*分配一个高速缓冲区,以后用来分配s3c2410_dma_buf*/
- 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); //通道寄存器基址
- /* point current stats somewhere */
- cp->stats = &cp->stats_store;
- cp->stats_store.timeout_shortest = LONG_MAX;
- /* basic channel configuration */
- /*设置加载的超时时间*/
- 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通道信息,内容如下:
- 151 struct s3c2410_dma_chan {
- 152 /* channel state flags and information */
- 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 /* channel state */
- 160
- 161 enum s3c2410_dma_state state;
- 162 enum s3c2410_dma_loadst load_state;
- 163 struct s3c2410_dma_client *client;
- 164
- 165 /* channel configuration */
- 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; /* channel flags */
- 171
- 172 struct s3c24xx_dma_map *map; /* channel hw maps */
- 173
- 174 /* channel's hardware position and configuration */
- 175 void __iomem *regs; /* channels registers */
- 176 void __iomem *addr_reg; /* data address register */
- 177 unsigned int irq; 中断号
- 178 unsigned long dcon; /默认控制寄存器的值
- 179
- 180 /* driver handles */
- 181 s3c2410_dma_cbfn_t callback_fn; 传输完成回调函数
- 182 s3c2410_dma_opfn_t op_fn; 操作完成回调函数*/
- 183
- 184 /* stats gathering */
- 185 struct s3c2410_dma_stats *stats;
- 186 struct s3c2410_dma_stats stats_store;
- 187
- 188 /* buffer list and information */
- 189 struct s3c2410_dma_buf *curr; /* current dma buffer */
- 190 struct s3c2410_dma_buf *next; /* next buffer to load */
- 191 struct s3c2410_dma_buf *end; /* end of queue */dma缓冲区链表
- 192
- 193 /* system device */
- 194 struct sys_device dev;
- 195 };
- 151 struct s3c2410_dma_chan {
- 152 /* channel state flags and information */
- 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 /* channel state */
- 160
- 161 enum s3c2410_dma_state state;
- 162 enum s3c2410_dma_loadst load_state;
- 163 struct s3c2410_dma_client *client;
- 164
- 165 /* channel configuration */
- 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; /* channel flags */
- 171
- 172 struct s3c24xx_dma_map *map; /* channel hw maps */
- 173
- 174 /* channel's hardware position and configuration */
- 175 void __iomem *regs; /* channels registers */
- 176 void __iomem *addr_reg; /* data address register */
- 177 unsigned int irq; 中断号
- 178 unsigned long dcon; /默认控制寄存器的值
- 179
- 180 /* driver handles */
- 181 s3c2410_dma_cbfn_t callback_fn; 传输完成回调函数
- 182 s3c2410_dma_opfn_t op_fn; 操作完成回调函数*/
- 183
- 184 /* stats gathering */
- 185 struct s3c2410_dma_stats *stats;
- 186 struct s3c2410_dma_stats stats_store;
- 187
- 188 /* buffer list and information */
- 189 struct s3c2410_dma_buf *curr; /* current dma buffer */
- 190 struct s3c2410_dma_buf *next; /* next buffer to load */
- 191 struct s3c2410_dma_buf *end; /* end of queue */dma缓冲区链表
- 192
- 193 /* system device */
- 194 struct sys_device dev;
- 195 };
(二)
先看下边一个结构体,s3c2410_dma_order。这个是建立目标板dma源与硬件的dma通道的关联。
- 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,
- },
- },
- },
- };
- 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(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;
- }
- 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;
- }
(三)
- 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);
- };
- 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(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,
- },
- };
DMA通道的使用:申请通道,申请中断,设置寄存器,安装回调函数,设置标志,将数据放入队列,最后就是调用static int s3c2410_dma_start(struct s3c2410_dma_chan *chan)来开始DMA的传输了。
首先看通道的申请:
- 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;
- }
- 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;
- }
下面的函数是找通道好,先在板子通道映射中找,再在芯片通道映射中找。
- static struct s3c2410_dma_chan *s3c2410_dma_map_channel(int channel)
- {
- struct s3c24xx_dma_order_ch *ord = NULL;
- struct s3c24xx_dma_map *ch_map;
- struct s3c2410_dma_chan *dmach;
- int ch;
- if (dma_sel.map == NULL || channel > dma_sel.map_size)
- return NULL;
- /*获得芯片的虚拟通道与真实通道映射的结构*/
- ch_map = dma_sel.map + channel;
- /* first, try the board mapping */
- /*如果有板子通道映射*/
- if (dma_order) {
- /*得到对应虚拟通道的所有真实通道的结构*/
- ord = &dma_order->channels[channel];
- /*找这个虚拟通道对应的每一个真实通道,看有没有有效并且未被使用的*/
- for (ch = 0; ch < dma_channels; ch++) {
- if (!is_channel_valid(ord->list[ch]))
- continue;
- if (s3c2410_chans[ord->list[ch]].in_use == 0) {
- ch = ord->list[ch] & ~DMA_CH_VALID;
- goto found;
- }
- }
- if (ord->flags & DMA_CH_NEVER)
- return NULL;
- }
- /*检查芯片虚拟通道与真实通道的映射,看有没有有效且未被使用的真实通道*/
- for (ch = 0; ch < dma_channels; ch++) {
- if (!is_channel_valid(ch_map->channels[ch]))
- continue;
- if (s3c2410_chans[ch].in_use == 0) {
- printk("mapped channel %d to %d/n", channel, ch);
- break;
- }
- }
- if (ch >= dma_channels)
- return NULL;
- /* update our channel mapping */
- found:
- /*将找到的通道保存在dmach中,并返回*/
- dmach = &s3c2410_chans[ch];
- dmach->map = ch_map;
- dma_chan_map[channel] = dmach;
- /* select the channel */
- /*调用选择通道的函数*/
- (dma_sel.select)(dmach, ch_map);
- return dmach;
- }
- static struct s3c2410_dma_chan *s3c2410_dma_map_channel(int channel)
- {
- struct s3c24xx_dma_order_ch *ord = NULL;
- struct s3c24xx_dma_map *ch_map;
- struct s3c2410_dma_chan *dmach;
- int ch;
- if (dma_sel.map == NULL || channel > dma_sel.map_size)
- return NULL;
- /*获得芯片的虚拟通道与真实通道映射的结构*/
- ch_map = dma_sel.map + channel;
- /* first, try the board mapping */
- /*如果有板子通道映射*/
- if (dma_order) {
- /*得到对应虚拟通道的所有真实通道的结构*/
- ord = &dma_order->channels[channel];
- /*找这个虚拟通道对应的每一个真实通道,看有没有有效并且未被使用的*/
- for (ch = 0; ch < dma_channels; ch++) {
- if (!is_channel_valid(ord->list[ch]))
- continue;
- if (s3c2410_chans[ord->list[ch]].in_use == 0) {
- ch = ord->list[ch] & ~DMA_CH_VALID;
- goto found;
- }
- }
- if (ord->flags & DMA_CH_NEVER)
- return NULL;
- }
- /*检查芯片虚拟通道与真实通道的映射,看有没有有效且未被使用的真实通道*/
- for (ch = 0; ch < dma_channels; ch++) {
- if (!is_channel_valid(ch_map->channels[ch]))
- continue;
- if (s3c2410_chans[ch].in_use == 0) {
- printk("mapped channel %d to %d/n", channel, ch);
- break;
- }
- }
- if (ch >= dma_channels)
- return NULL;
- /* update our channel mapping */
- found:
- /*将找到的通道保存在dmach中,并返回*/
- dmach = &s3c2410_chans[ch];
- dmach->map = ch_map;
- dma_chan_map[channel] = dmach;
- /* select the channel */
- /*调用选择通道的函数*/
- (dma_sel.select)(dmach, ch_map);
- return dmach;
- }
设置寄存器,设置寄存器的工作由s3c2410_dma_devconfig和s3c2410_dma_config完成:
- int s3c2410_dma_devconfig(int channel,
- enum s3c2410_dmasrc source,
- 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",
- __func__, (int)source, hwcfg, devaddr);
- chan->source = source; //保存dma源
- chan->dev_addr = devaddr; //保存dma源地址
- chan->hw_cfg = hwcfg; //保存dma源的控制信息
- switch (source) {
- case S3C2410_DMASRC_HW: //源是外设,从外设读数据到内存,源的地址是固定的
- /* source is hardware */
- pr_debug("%s: hw source, devaddr=%08lx, hwcfg=%d/n",
- __func__, 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)); //目的地在AHB总线上
- chan->addr_reg = dma_regaddr(chan, S3C2410_DMA_DIDST);
- break;
- case S3C2410_DMASRC_MEM: //源是内存,从内存读数据到外设上,目的地址是固定的
- /* source is memory */
- pr_debug("%s: mem source, devaddr=%08lx, hwcfg=%d/n",
- __func__, devaddr, hwcfg);
- dma_wrreg(chan, S3C2410_DMA_DISRCC, (0<<1) | (0<<0)); //目的地址在AHB总线上
- dma_wrreg(chan, S3C2410_DMA_DIDST, devaddr); //把目的地址写到初始目的寄存器
- dma_wrreg(chan, S3C2410_DMA_DIDSTC, hwcfg & 3); //初始化目的控制寄存器
- chan->addr_reg = dma_regaddr(chan, S3C2410_DMA_DISRC);
- break;
- /*无论内存是源还是目的,这个地址始终是保存在chan->addr_reg*/
- default:
- printk(KERN_ERR "dma%d: invalid source type (%d)/n",
- channel, source);
- return -EINVAL;
- }
- if (dma_sel.direction != NULL)
- (dma_sel.direction)(chan, chan->map, source);
- return 0;
- }
- int s3c2410_dma_devconfig(int channel,
- enum s3c2410_dmasrc source,
- 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",
- __func__, (int)source, hwcfg, devaddr);
- chan->source = source; //保存dma源
- chan->dev_addr = devaddr; //保存dma源地址
- chan->hw_cfg = hwcfg; //保存dma源的控制信息
- switch (source) {
- case S3C2410_DMASRC_HW: //源是外设,从外设读数据到内存,源的地址是固定的
- /* source is hardware */
- pr_debug("%s: hw source, devaddr=%08lx, hwcfg=%d/n",
- __func__, 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)); //目的地在AHB总线上
- chan->addr_reg = dma_regaddr(chan, S3C2410_DMA_DIDST);
- break;
- case S3C2410_DMASRC_MEM: //源是内存,从内存读数据到外设上,目的地址是固定的
- /* source is memory */
- pr_debug("%s: mem source, devaddr=%08lx, hwcfg=%d/n",
- __func__, devaddr, hwcfg);
- dma_wrreg(chan, S3C2410_DMA_DISRCC, (0<<1) | (0<<0)); //目的地址在AHB总线上
- dma_wrreg(chan, S3C2410_DMA_DIDST, devaddr); //把目的地址写到初始目的寄存器
- dma_wrreg(chan, S3C2410_DMA_DIDSTC, hwcfg & 3); //初始化目的控制寄存器
- chan->addr_reg = dma_regaddr(chan, S3C2410_DMA_DISRC);
- break;
- /*无论内存是源还是目的,这个地址始终是保存在chan->addr_reg*/
- default:
- printk(KERN_ERR "dma%d: invalid source type (%d)/n",
- channel, source);
- return -EINVAL;
- }
- if (dma_sel.direction != NULL)
- (dma_sel.direction)(chan, chan->map, source);
- return 0;
- }
- int s3c2410_dma_config(unsigned int channel,
- int xferunit,
- int dcon)
- {
- /*找到虚拟通道对应的实际通道*/
- struct s3c2410_dma_chan *chan = lookup_dma_channel(channel);
- pr_debug("%s: chan=%d, xfer_unit=%d, dcon=%08x/n",
- __func__, channel, xferunit, dcon);
- if (chan == NULL)
- return -EINVAL;
- pr_debug("%s: Initial dcon is %08x/n", __func__, dcon);
- /*清除DMA源的选择位*/
- dcon |= chan->dcon & dma_sel.dcon_mask;
- pr_debug("%s: New dcon is %08x/n", __func__, 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", __func__, xferunit);
- return -EINVAL;
- }
- dcon |= S3C2410_DCON_HWTRIG; //DMA源是硬件
- dcon |= S3C2410_DCON_INTREQ; //中断使能
- pr_debug("%s: dcon now %08x/n", __func__, dcon);
- /*将通道控制寄存器和传输大小存于chan中*/
- chan->dcon = dcon;
- chan->xfer_unit = xferunit;
- return 0;
- }
- int s3c2410_dma_config(unsigned int channel,
- int xferunit,
- int dcon)
- {
- /*找到虚拟通道对应的实际通道*/
- struct s3c2410_dma_chan *chan = lookup_dma_channel(channel);
- pr_debug("%s: chan=%d, xfer_unit=%d, dcon=%08x/n",
- __func__, channel, xferunit, dcon);
- if (chan == NULL)
- return -EINVAL;
- pr_debug("%s: Initial dcon is %08x/n", __func__, dcon);
- /*清除DMA源的选择位*/
- dcon |= chan->dcon & dma_sel.dcon_mask;
- pr_debug("%s: New dcon is %08x/n", __func__, 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", __func__, xferunit);
- return -EINVAL;
- }
- dcon |= S3C2410_DCON_HWTRIG; //DMA源是硬件
- dcon |= S3C2410_DCON_INTREQ; //中断使能
- pr_debug("%s: dcon now %08x/n", __func__, dcon);
- /*将通道控制寄存器和传输大小存于chan中*/
- chan->dcon = dcon;
- chan->xfer_unit = xferunit;
- return 0;
- }
设置回调函数:
- int s3c2410_dma_set_buffdone_fn(unsigned int channel, s3c2410_dma_cbfn_t rtn)
- {
- 。。。。。。。
- chan->callback_fn = rtn;
- return 0;
- }
- int s3c2410_dma_set_buffdone_fn(unsigned int channel, s3c2410_dma_cbfn_t rtn)
- {
- 。。。。。。。
- chan->callback_fn = rtn;
- return 0;
- }
设置标志:
- int s3c2410_dma_setflags(unsigned int channel, unsigned int flags)
- {
- 。。。。。。。。。。。。。。
- chan->flags = flags;
- return 0;
- }
- int s3c2410_dma_setflags(unsigned int channel, unsigned int flags)
- {
- 。。。。。。。。。。。。。。
- chan->flags = flags;
- return 0;
- }
将数据放入队列,先看一下一个结构:
- struct s3c2410_dma_buf {
- struct s3c2410_dma_buf *next;
- int magic; /* magic */
- int size; /* buffer size in bytes */
- dma_addr_t data; /* start of DMA data */
- dma_addr_t ptr; /* where the DMA got to [1] */
- void *id; /* client's id */
- };
- struct s3c2410_dma_buf {
- struct s3c2410_dma_buf *next;
- int magic; /* magic */
- int size; /* buffer size in bytes */
- dma_addr_t data; /* start of DMA data */
- dma_addr_t ptr; /* where the DMA got to [1] */
- void *id; /* client's id */
- };
每个struct s3c2410_dma_chan维护了一个缓冲区队列,每个缓冲区用上边的结构表示。在struct s3c2410_dma_chan中的结构是:
- /* buffer list and information */
- struct s3c2410_dma_buf *curr; /* current dma buffer */
- struct s3c2410_dma_buf *next; /* next buffer to load */
- struct s3c2410_dma_buf *end; /* end of queue */
- /* buffer list and information */
- struct s3c2410_dma_buf *curr; /* current dma buffer */
- struct s3c2410_dma_buf *next; /* next buffer to load */
- struct s3c2410_dma_buf *end; /* end of queue */
下边这个函数就是完成将s3c2410_dma_buf放入这个队列中排队:
- int s3c2410_dma_enqueue(unsigned int channel, void *id,
- dma_addr_t data, int size)
- {
- /*找到虚拟通道对应的实际通道*/
- struct s3c2410_dma_chan *chan = lookup_dma_channel(channel);
- struct s3c2410_dma_buf *buf;
- unsigned long flags;
- if (chan == NULL)
- return -EINVAL;
- pr_debug("%s: id=%p, data=%08x, size=%d/n",
- __func__, id, (unsigned int)data, size);
- /*分配s3c2410_dma_chan结构的buffer*/
- buf = kmem_cache_alloc(dma_kmem, GFP_ATOMIC);
- if (buf == NULL) {
- pr_debug("%s: out of memory (%ld alloc)/n",
- __func__, (long)sizeof(*buf));
- return -ENOMEM;
- }
- //pr_debug("%s: new buffer %p/n", __func__, buf);
- //dbg_showchan(chan);
- /*设置这个buffer*/
- buf->next = NULL;
- buf->data = buf->ptr = data; //指向要传输数据的地址
- buf->size = size; //该段buffer的大小
- buf->id = id;
- buf->magic = BUF_MAGIC;
- local_irq_save(flags);
- /*加载的是该通道的第一段buf*/
- if (chan->curr == NULL) {
- /* we've got nothing loaded... */
- pr_debug("%s: buffer %p queued onto empty channel/n",
- __func__, buf);
- chan->curr = buf; //curr指向现在生成的buf
- chan->end = buf;
- chan->next = NULL;
- } else {
- pr_debug("dma%d: %s: buffer %p queued onto non-empty channel/n",
- chan->number, __func__, buf);
- if (chan->end == NULL)
- pr_debug("dma%d: %s: %p not empty, and chan->end==NULL?/n",
- chan->number, __func__, chan);
- /*从链表尾加入链表*/
- chan->end->next = buf;
- chan->end = buf;
- }
- /* if necessary, update the next buffer field */
- if (chan->next == NULL)
- chan->next = buf;
- if (chan->state == S3C2410_DMA_RUNNING) { //该channel正在运行
- if (chan->load_state == S3C2410_DMALOAD_1LOADED && 1) { //已有buf load了
- if (s3c2410_dma_waitforload(chan, __LINE__) == 0) { //等待load
- printk(KERN_ERR "dma%d: loadbuffer:"
- "timeout loading buffer/n",
- chan->number);
- dbg_showchan(chan);
- local_irq_restore(flags);
- return -EINVAL;
- }
- }
- while (s3c2410_dma_canload(chan) && chan->next != NULL) { //检查能否load
- s3c2410_dma_loadbuffer(chan, chan->next); //load buffer
- }
- } else if (chan->state == S3C2410_DMA_IDLE) { //该channel空闲着
- if (chan->flags & S3C2410_DMAF_AUTOSTART) { //如果设了自动启动标记,则直接启动该次传输
- s3c2410_dma_ctrl(chan->number | DMACH_LOW_LEVEL, //启动传输
- S3C2410_DMAOP_START);
- }
- }
- local_irq_restore(flags);
- return 0;
- }
- int s3c2410_dma_enqueue(unsigned int channel, void *id,
- dma_addr_t data, int size)
- {
- /*找到虚拟通道对应的实际通道*/
- struct s3c2410_dma_chan *chan = lookup_dma_channel(channel);
- struct s3c2410_dma_buf *buf;
- unsigned long flags;
- if (chan == NULL)
- return -EINVAL;
- pr_debug("%s: id=%p, data=%08x, size=%d/n",
- __func__, id, (unsigned int)data, size);
- /*分配s3c2410_dma_chan结构的buffer*/
- buf = kmem_cache_alloc(dma_kmem, GFP_ATOMIC);
- if (buf == NULL) {
- pr_debug("%s: out of memory (%ld alloc)/n",
- __func__, (long)sizeof(*buf));
- return -ENOMEM;
- }
- //pr_debug("%s: new buffer %p/n", __func__, buf);
- //dbg_showchan(chan);
- /*设置这个buffer*/
- buf->next = NULL;
- buf->data = buf->ptr = data; //指向要传输数据的地址
- buf->size = size; //该段buffer的大小
- buf->id = id;
- buf->magic = BUF_MAGIC;
- local_irq_save(flags);
- /*加载的是该通道的第一段buf*/
- if (chan->curr == NULL) {
- /* we've got nothing loaded... */
- pr_debug("%s: buffer %p queued onto empty channel/n",
- __func__, buf);
- chan->curr = buf; //curr指向现在生成的buf
- chan->end = buf;
- chan->next = NULL;
- } else {
- pr_debug("dma%d: %s: buffer %p queued onto non-empty channel/n",
- chan->number, __func__, buf);
- if (chan->end == NULL)
- pr_debug("dma%d: %s: %p not empty, and chan->end==NULL?/n",
- chan->number, __func__, chan);
- /*从链表尾加入链表*/
- chan->end->next = buf;
- chan->end = buf;
- }
- /* if necessary, update the next buffer field */
- if (chan->next == NULL)
- chan->next = buf;
- if (chan->state == S3C2410_DMA_RUNNING) { //该channel正在运行
- if (chan->load_state == S3C2410_DMALOAD_1LOADED && 1) { //已有buf load了
- if (s3c2410_dma_waitforload(chan, __LINE__) == 0) { //等待load
- printk(KERN_ERR "dma%d: loadbuffer:"
- "timeout loading buffer/n",
- chan->number);
- dbg_showchan(chan);
- local_irq_restore(flags);
- return -EINVAL;
- }
- }
- while (s3c2410_dma_canload(chan) && chan->next != NULL) { //检查能否load
- s3c2410_dma_loadbuffer(chan, chan->next); //load buffer
- }
- } else if (chan->state == S3C2410_DMA_IDLE) { //该channel空闲着
- if (chan->flags & S3C2410_DMAF_AUTOSTART) { //如果设了自动启动标记,则直接启动该次传输
- s3c2410_dma_ctrl(chan->number | DMACH_LOW_LEVEL, //启动传输
- S3C2410_DMAOP_START);
- }
- }
- local_irq_restore(flags);
- return 0;
- }
channel在运行的时候会有很多状态,在arch/arm/mach-s3c2410/include/mach/dma.h,注意已经很清楚了,我就不多解释了。
- /* enum s3c2410_dma_loadst
- *
- * This represents the state of the DMA engine, wrt to the loaded / running
- * transfers. Since we don't have any way of knowing exactly the state of
- * the DMA transfers, we need to know the state to make decisions on wether
- * we can
- *
- * S3C2410_DMA_NONE
- *
- * There are no buffers loaded (the channel should be inactive)
- *
- * S3C2410_DMA_1LOADED
- *
- * There is one buffer loaded, however it has not been confirmed to be
- * loaded by the DMA engine. This may be because the channel is not
- * yet running, or the DMA driver decided that it was too costly to
- * sit and wait for it to happen.
- *
- * S3C2410_DMA_1RUNNING
- *
- * The buffer has been confirmed running, and not finisged
- *
- * S3C2410_DMA_1LOADED_1RUNNING
- *
- * There is a buffer waiting to be loaded by the DMA engine, and one
- * currently running.
- */
- enum s3c2410_dma_loadst {
- S3C2410_DMALOAD_NONE,
- S3C2410_DMALOAD_1LOADED,
- S3C2410_DMALOAD_1RUNNING,
- S3C2410_DMALOAD_1LOADED_1RUNNING,
- };
- /* enum s3c2410_dma_loadst
- *
- * This represents the state of the DMA engine, wrt to the loaded / running
- * transfers. Since we don't have any way of knowing exactly the state of
- * the DMA transfers, we need to know the state to make decisions on wether
- * we can
- *
- * S3C2410_DMA_NONE
- *
- * There are no buffers loaded (the channel should be inactive)
- *
- * S3C2410_DMA_1LOADED
- *
- * There is one buffer loaded, however it has not been confirmed to be
- * loaded by the DMA engine. This may be because the channel is not
- * yet running, or the DMA driver decided that it was too costly to
- * sit and wait for it to happen.
- *
- * S3C2410_DMA_1RUNNING
- *
- * The buffer has been confirmed running, and not finisged
- *
- * S3C2410_DMA_1LOADED_1RUNNING
- *
- * There is a buffer waiting to be loaded by the DMA engine, and one
- * currently running.
- */
- enum s3c2410_dma_loadst {
- S3C2410_DMALOAD_NONE,
- S3C2410_DMALOAD_1LOADED,
- S3C2410_DMALOAD_1RUNNING,
- S3C2410_DMALOAD_1LOADED_1RUNNING,
- };
中断处理函数:
- static irqreturn_t
- s3c2410_dma_irq(int irq, void *devpw)
- {
- struct s3c2410_dma_chan *chan = (struct s3c2410_dma_chan *)devpw;
- struct s3c2410_dma_buf *buf;
- buf = chan->curr; //当前传输完毕的buf
- dbg_showchan(chan);
- /* modify the channel state */
- switch (chan->load_state) { //改变状态,如果对上边那4个状态理解了很容易看懂的
- case S3C2410_DMALOAD_1RUNNING:
- /* TODO - if we are running only one buffer, we probably
- * want to reload here, and then worry about the buffer
- * callback */
- chan->load_state = S3C2410_DMALOAD_NONE;
- break;
- case S3C2410_DMALOAD_1LOADED:
- /* iirc, we should go back to NONE loaded here, we
- * had a buffer, and it was never verified as being
- * loaded.
- */
- chan->load_state = S3C2410_DMALOAD_NONE;
- break;
- case S3C2410_DMALOAD_1LOADED_1RUNNING:
- /* we'll worry about checking to see if another buffer is
- * ready after we've called back the owner. This should
- * ensure we do not wait around too long for the DMA
- * engine to start the next transfer
- */
- chan->load_state = S3C2410_DMALOAD_1LOADED;
- break;
- case S3C2410_DMALOAD_NONE:
- printk(KERN_ERR "dma%d: IRQ with no loaded buffer?/n",
- chan->number);
- break;
- default:
- printk(KERN_ERR "dma%d: IRQ in invalid load_state %d/n",
- chan->number, chan->load_state);
- break;
- }
- if (buf != NULL) { //如果不为空
- /* update the chain to make sure that if we load any more
- * buffers when we call the callback function, things should
- * work properly */
- chan->curr = buf->next; //指向下一个buf
- buf->next = NULL;
- if (buf->magic != BUF_MAGIC) {
- printk(KERN_ERR "dma%d: %s: buf %p incorrect magic/n",
- chan->number, __func__, buf);
- return IRQ_HANDLED;
- }
- s3c2410_dma_buffdone(chan, buf, S3C2410_RES_OK); //buf传输完成后的操作
- /* free resouces */
- s3c2410_dma_freebuf(buf); //释放buf
- } else {
- }
- /* only reload if the channel is still running... our buffer done
- * routine may have altered the state by requesting the dma channel
- * to stop or shutdown... */
- /* todo: check that when the channel is shut-down from inside this
- * function, we cope with unsetting reload, etc */
- if (chan->next != NULL && chan->state != S3C2410_DMA_IDLE) { //还有要传输的buf,则继续传输
- unsigned long flags;
- switch (chan->load_state) {
- case S3C2410_DMALOAD_1RUNNING:
- /* don't need to do anything for this state */
- break;
- case S3C2410_DMALOAD_NONE:
- /* can load buffer immediately */
- break;
- case S3C2410_DMALOAD_1LOADED:
- if (s3c2410_dma_waitforload(chan, __LINE__) == 0) { //如果已经有载入的,则等待被载入
- /* flag error? */
- printk(KERN_ERR "dma%d: timeout waiting for load (%s)/n",
- chan->number, __func__);
- return IRQ_HANDLED;
- }
- break;
- case S3C2410_DMALOAD_1LOADED_1RUNNING:
- goto no_load;
- default:
- printk(KERN_ERR "dma%d: unknown load_state in irq, %d/n",
- chan->number, chan->load_state);
- return IRQ_HANDLED;
- }
- local_irq_save(flags);
- s3c2410_dma_loadbuffer(chan, chan->next); //载入buf
- local_irq_restore(flags);
- } else { //所有传输完成
- s3c2410_dma_lastxfer(chan); //完成处理工作
- /* see if we can stop this channel.. */
- if (chan->load_state == S3C2410_DMALOAD_NONE) {
- pr_debug("dma%d: end of transfer, stopping channel (%ld)/n",
- chan->number, jiffies);
- s3c2410_dma_ctrl(chan->number | DMACH_LOW_LEVEL, //停止DMA传输
- S3C2410_DMAOP_STOP);
- }
- }
- no_load:
- return IRQ_HANDLED;
- }
- static irqreturn_t
- s3c2410_dma_irq(int irq, void *devpw)
- {
- struct s3c2410_dma_chan *chan = (struct s3c2410_dma_chan *)devpw;
- struct s3c2410_dma_buf *buf;
- buf = chan->curr; //当前传输完毕的buf
- dbg_showchan(chan);
- /* modify the channel state */
- switch (chan->load_state) { //改变状态,如果对上边那4个状态理解了很容易看懂的
- case S3C2410_DMALOAD_1RUNNING:
- /* TODO - if we are running only one buffer, we probably
- * want to reload here, and then worry about the buffer
- * callback */
- chan->load_state = S3C2410_DMALOAD_NONE;
- break;
- case S3C2410_DMALOAD_1LOADED:
- /* iirc, we should go back to NONE loaded here, we
- * had a buffer, and it was never verified as being
- * loaded.
- */
- chan->load_state = S3C2410_DMALOAD_NONE;
- break;
- case S3C2410_DMALOAD_1LOADED_1RUNNING:
- /* we'll worry about checking to see if another buffer is
- * ready after we've called back the owner. This should
- * ensure we do not wait around too long for the DMA
- * engine to start the next transfer
- */
- chan->load_state = S3C2410_DMALOAD_1LOADED;
- break;
- case S3C2410_DMALOAD_NONE:
- printk(KERN_ERR "dma%d: IRQ with no loaded buffer?/n",
- chan->number);
- break;
- default:
- printk(KERN_ERR "dma%d: IRQ in invalid load_state %d/n",
- chan->number, chan->load_state);
- break;
- }
- if (buf != NULL) { //如果不为空
- /* update the chain to make sure that if we load any more
- * buffers when we call the callback function, things should
- * work properly */
- chan->curr = buf->next; //指向下一个buf
- buf->next = NULL;
- if (buf->magic != BUF_MAGIC) {
- printk(KERN_ERR "dma%d: %s: buf %p incorrect magic/n",
- chan->number, __func__, buf);
- return IRQ_HANDLED;
- }
- s3c2410_dma_buffdone(chan, buf, S3C2410_RES_OK); //buf传输完成后的操作
- /* free resouces */
- s3c2410_dma_freebuf(buf); //释放buf
- } else {
- }
- /* only reload if the channel is still running... our buffer done
- * routine may have altered the state by requesting the dma channel
- * to stop or shutdown... */
- /* todo: check that when the channel is shut-down from inside this
- * function, we cope with unsetting reload, etc */
- if (chan->next != NULL && chan->state != S3C2410_DMA_IDLE) { //还有要传输的buf,则继续传输
- unsigned long flags;
- switch (chan->load_state) {
- case S3C2410_DMALOAD_1RUNNING:
- /* don't need to do anything for this state */
- break;
- case S3C2410_DMALOAD_NONE:
- /* can load buffer immediately */
- break;
- case S3C2410_DMALOAD_1LOADED:
- if (s3c2410_dma_waitforload(chan, __LINE__) == 0) { //如果已经有载入的,则等待被载入
- /* flag error? */
- printk(KERN_ERR "dma%d: timeout waiting for load (%s)/n",
- chan->number, __func__);
- return IRQ_HANDLED;
- }
- break;
- case S3C2410_DMALOAD_1LOADED_1RUNNING:
- goto no_load;
- default:
- printk(KERN_ERR "dma%d: unknown load_state in irq, %d/n",
- chan->number, chan->load_state);
- return IRQ_HANDLED;
- }
- local_irq_save(flags);
- s3c2410_dma_loadbuffer(chan, chan->next); //载入buf
- local_irq_restore(flags);
- } else { //所有传输完成
- s3c2410_dma_lastxfer(chan); //完成处理工作
- /* see if we can stop this channel.. */
- if (chan->load_state == S3C2410_DMALOAD_NONE) {
- pr_debug("dma%d: end of transfer, stopping channel (%ld)/n",
- chan->number, jiffies);
- s3c2410_dma_ctrl(chan->number | DMACH_LOW_LEVEL, //停止DMA传输
- S3C2410_DMAOP_STOP);
- }
- }
- no_load:
- return IRQ_HANDLED;
- }
可以选择不同的dma操作:
- int
- s3c2410_dma_ctrl(unsigned int channel, enum s3c2410_chan_op op)
- {
- struct s3c2410_dma_chan *chan = lookup_dma_channel(channel);
- if (chan == NULL)
- return -EINVAL;
- switch (op) {
- case S3C2410_DMAOP_START:
- return s3c2410_dma_start(chan);
- case S3C2410_DMAOP_STOP:
- return s3c2410_dma_dostop(chan);
- case S3C2410_DMAOP_PAUSE:
- case S3C2410_DMAOP_RESUME:
- return -ENOENT;
- case S3C2410_DMAOP_FLUSH:
- return s3c2410_dma_flush(chan);
- case S3C2410_DMAOP_STARTED:
- return s3c2410_dma_started(chan);
- case S3C2410_DMAOP_TIMEOUT:
- return 0;
- }
- return -ENOENT; /* unknown, don't bother */
- }