自娱自乐9之Linux DMA使用1(三星平台DMA分析)

和以前一样,我不说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 <[email protected]>
 *	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 <[email protected]>
 *
 * 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
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值