和以前一样,我不说dma基础知识,你可以看看ldd3
这次我说的是三星平台的dma,不是三星的某款芯片的dma使用。这主要得益于三星公司统一了接口。比如我后有的例子是在s3c2440上做的但是我是参考s3c64xx的spi驱动。
当然内核还是linux-3.2.36,我们看dma-ops.h
/* arch/arm/plat-samsung/include/plat/dma-ops.h
*
* Copyright (c) 2011 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* Samsung DMA support
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef __SAMSUNG_DMA_OPS_H_
#define __SAMSUNG_DMA_OPS_H_ __FILE__
#include <linux/dmaengine.h>
struct samsung_dma_prep_info {
enum dma_transaction_type cap;//dma处理类型
enum dma_data_direction direction;//dma传输方向
dma_addr_t buf;//内存地址
unsigned long period;//
unsigned long len;//buf长度,sizeof(buf) * width,width在下面struct samsung_dma_info
/*
.c中调用
int len = (info->cap == DMA_CYCLIC) ? info->period : info->len;
...
我不是太清楚period和len区别
*/
void (*fp)(void *data);//dma中断时会调用,一般作为dma传输完成的接口
void *fp_param;//传入上面fp的参数
};
struct samsung_dma_info {
enum dma_transaction_type cap;//dma处理类型
/*
if (info->cap == DMA_CYCLIC)
s3c2410_dma_setflags(dma_ch, S3C2410_DMAF_CIRCULAR);//chan->flags设置
这个可能和芯片有点关系
我的plat-s3c24xx中,通道请求函数
if (chan->flags & S3C2410_DMAF_AUTOSTART) {//如果设置为自动运行,就调用start函数
s3c2410_dma_ctrl(chan->number | DMACH_LOW_LEVEL,
S3C2410_DMAOP_START);
}
没有判断S3C2410_DMAF_CIRCULAR标志
*/
enum dma_data_direction direction;//dma传输方向
enum dma_slave_buswidth width;//要传输的数据宽度,就是(字节、半字、字)
dma_addr_t fifo;//外设地址
struct s3c2410_dma_client *client;
/*
struct s3c2410_dma_client {
char *name;
};
就是一个name,你申请通道时命名就可以了,主要dma中断注册是用、free通道时判断
是不是正确通道
*/
};
struct samsung_dma_ops {
unsigned (*request)(enum dma_ch ch, struct samsung_dma_info *info);//请求会有些限制
//1.总是认为我们的外围设备是一个固定的地址
//2.总是认为我们的内存地址增加
//3.硬件触发
//4.所有传输完成产生中断
//上面这个从数据手册上看是可以设的,但是三星写的驱动代码没有选的可能
int (*release)(unsigned ch, struct s3c2410_dma_client *client);//释放
int (*prepare)(unsigned ch, struct samsung_dma_prep_info *info);//准备
//准备会把内存数据加入链表中,如果有数据在传输,会打开再加载开关
int (*trigger)(unsigned ch);//触发
int (*started)(unsigned ch);//再次开始,实际就是再次载入数据
int (*flush)(unsigned ch);//清除通道数据
int (*stop)(unsigned ch);//停止
};
extern void *samsung_dmadev_get_ops(void);
extern void *s3c_dma_get_ops(void);
//去获取一个struct samsung_dma_ops全局变量,
//然后调用驱动,这个倒是给我们提供了一种驱动之间调用的方法
static inline void *__samsung_dma_get_ops(void)
{
if (samsung_dma_is_dmadev())
return samsung_dmadev_get_ops();
else
return s3c_dma_get_ops();
}
/*
* samsung_dma_get_ops
* get the set of samsung dma operations
*/
//在驱动中调用这个
#define samsung_dma_get_ops() __samsung_dma_get_ops()
#endif /* __SAMSUNG_DMA_OPS_H_ */
如果你只是想看看应用接口,你可以在此打住,直接看《自娱自乐10》中dma使用的例子,增加你的理解。
如果你和我一样死脑筋,你可以看看下面的源码分析:主要有三个文件
下面这个文件,看的时候不要纠结,主要是为第二个通道使用的,知道就行。
/* linux/arch/arm/plat-samsung/dma.c
*
* Copyright (c) 2003-2009 Simtec Electronics
* Ben Dooks <ben@simtec.co.uk>
* http://armlinux.simtec.co.uk/
*
* S3C DMA core
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
struct s3c2410_dma_buf;
/*
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
};
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <mach/dma.h>
#include <mach/irqs.h>
/* dma channel state information */
struct s3c2410_dma_chan s3c2410_chans[S3C_DMA_CHANNELS];
struct s3c2410_dma_chan *s3c_dma_chan_map[DMACH_MAX];
/* s3c_dma_lookup_channel
*
* change the dma channel number given into a real dma channel id
*/
//查找对应通道的struct s3c2410_dma_chan
//这个通道不是dma每个通道,是指外设对应的。
struct s3c2410_dma_chan *s3c_dma_lookup_channel(unsigned int channel)
{
if (channel & DMACH_LOW_LEVEL)
return &s3c2410_chans[channel & ~DMACH_LOW_LEVEL];
else
return s3c_dma_chan_map[channel];
}
/* do we need to protect the settings of the fields from
* irq?
*/
int s3c2410_dma_set_opfn(enum dma_ch channel, s3c2410_dma_opfn_t rtn)
{
struct s3c2410_dma_chan *chan = s3c_dma_lookup_channel(channel);
if (chan == NULL)
return -EINVAL;
pr_debug("%s: chan=%p, op rtn=%p\n", __func__, chan, rtn);
chan->op_fn = rtn;
//从下面可以看出在开始和结束时会调用这个函数
return 0;
}
EXPORT_SYMBOL(s3c2410_dma_set_opfn);
int s3c2410_dma_set_buffdone_fn(enum dma_ch channel, s3c2410_dma_cbfn_t rtn)
{
struct s3c2410_dma_chan *chan = s3c_dma_lookup_channel(channel);
if (chan == NULL)
return -EINVAL;
pr_debug("%s: chan=%p, callback rtn=%p\n", __func__, chan, rtn);
//在s3c2410_dma_flush和中断函数中调用
chan->callback_fn = rtn;
return 0;
}
EXPORT_SYMBOL(s3c2410_dma_set_buffdone_fn);
//设置标志
int s3c2410_dma_setflags(enum dma_ch channel, unsigned int flags)
{
struct s3c2410_dma_chan *chan = s3c_dma_lookup_channel(channel);
if (chan == NULL)
return -EINVAL;
chan->flags = flags;
return 0;
}
EXPORT_SYMBOL(s3c2410_dma_setflags);
下面是plat-s3c24xx中的,应该只适用s3c24xx芯片
文件有点大,我提供一个看的方法
1. 看init函数。
2. 看request函数。
3. 看s3c2410_dma_ctrl,里面有停止、开始等。
4. 看中断函数。
5. 看电源管理。
/* linux/arch/arm/plat-s3c24xx/dma.c
*
* Copyright 2003-2006 Simtec Electronics
* Ben Dooks <ben@simtec.co.uk>
*
* S3C2410 DMA core
*
* http://armlinux.simtec.co.uk/
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifdef CONFIG_S3C2410_DMA_DEBUG
#define DEBUG
#endif
#include <linux/module.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/spinlock.h>
#include <linux/interrupt.h>
#include <linux/syscore_ops.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/io.h>
#include <asm/system.h>
#include <asm/irq.h>
#include <mach/hardware.h>
#include <mach/dma.h>
#include <mach/map.h>
#include <plat/dma-s3c24xx.h>
#include <plat/regs-dma.h>
/* io map for dma */
static void __iomem *dma_base;
static struct kmem_cache *dma_kmem;
static int dma_channels;
static struct s3c24xx_dma_selection dma_sel;
//调试功能就不看了
/* debugging functions */
#define BUF_MAGIC (0xcafebabe)
#define dmawarn(fmt...) printk(KERN_DEBUG fmt)
#define dma_regaddr(chan, reg) ((chan)->regs + (reg))
#if 1
#define dma_wrreg(chan, reg, val) writel((val), (chan)->regs + (reg))
#else
static inline void
dma_wrreg(struct s3c2410_dma_chan *chan, int reg, unsigned long val)
{
pr_debug("writing %08x to register %08x\n",(unsigned int)val,reg);
writel(val, dma_regaddr(chan, reg));
}
#endif
#define dma_rdreg(chan, reg) readl((chan)->regs + (reg))
/* captured register state for debug */
struct s3c2410_dma_regstate {
unsigned long dcsrc; //源
unsigned long disrc; //目标
unsigned long dstat; //状态
unsigned long dcon; //配置
unsigned long dmsktrig; //触发屏蔽
};
//下面还是调试,不看了
#ifdef CONFIG_S3C2410_DMA_DEBUG
/* dmadbg_showregs
*
* simple debug routine to print the current state of the dma registers
*/
static void
dmadbg_capture(struct s3c2410_dma_chan *chan, struct s3c2410_dma_regstate *regs)
{
regs->dcsrc = dma_rdreg(chan, S3C2410_DMA_DCSRC);
regs->disrc = dma_rdreg(chan, S3C2410_DMA_DISRC);
regs->dstat = dma_rdreg(chan, S3C2410_DMA_DSTAT);
regs->dcon = dma_rdreg(chan, S3C2410_DMA_DCON);
regs->dmsktrig = dma_rdreg(chan, S3C2410_DMA_DMASKTRIG);
}
static void
dmadbg_dumpregs(const char *fname, int line, struct s3c2410_dma_chan *chan,
struct s3c2410_dma_regstate *regs)
{
printk(KERN_DEBUG "dma%d: %s:%d: DCSRC=%08lx, DISRC=%08lx, DSTAT=%08lx DMT=%02lx, DCON=%08lx\n",
chan->number, fname, line,
regs->dcsrc, regs->disrc, regs->dstat, regs->dmsktrig,
regs->dcon);
}
static void
dmadbg_showchan(const char *fname, int line, struct s3c2410_dma_chan *chan)
{
struct s3c2410_dma_regstate state;
dmadbg_capture(chan, &state);
printk(KERN_DEBUG "dma%d: %s:%d: ls=%d, cur=%p, %p %p\n",
chan->number, fname, line, chan->load_state,
chan->curr, chan->next, chan->end);
dmadbg_dumpregs(fname, line, chan, &state);
}
static void
dmadbg_showregs(const char *fname, int line, struct s3c2410_dma_chan *chan)
{
struct s3c2410_dma_regstate state;
dmadbg_capture(chan, &state);
dmadbg_dumpregs(fname, line, chan, &state);
}
#define dbg_showregs(chan) dmadbg_showregs(__func__, __LINE__, (chan))
#define dbg_showchan(chan) dmadbg_showchan(__func__, __LINE__, (chan))
#else
#define dbg_showregs(chan) do { } while(0)
#define dbg_showchan(chan) do { } while(0)
#endif /* CONFIG_S3C2410_DMA_DEBUG */
/* s3c2410_dma_stats_timeout
*
* Update DMA stats from timeout info
*/
//记录dma传输用时
static void
s3c2410_dma_stats_timeout(struct s3c2410_dma_stats *stats, int val)
{
if (stats == NULL)
return;
if (val > stats->timeout_longest)
stats->timeout_longest = val;
if (val < stats->timeout_shortest)
stats->timeout_shortest = val;
stats->timeout_avg += val;
}
/* s3c2410_dma_waitforload
*
* wait for the DMA engine to load a buffer, and update the state accordingly
*/
//等待DMA引擎载入一个缓冲
static int
s3c2410_dma_waitforload(struct s3c2410_dma_chan *chan, int line)
{
int timeout = chan->load_timeout;//1 << 18
int took;
if (chan->l