s3c2440中DMA的一般操作步骤分七步:
s3c2410_dma_client 的定义为:
struct s3c2410_dma_client {
};
void* dev一般设置为NULL。channel是通道号。
根据xferunit以及dcon设置通道的控制寄存器DCONx
xferunit 为每次传输的数据大小:0:byte 1:half word 2:word
xferunit 为每次传输的数据大小:0:byte 1:half word 2:word
设置相应的dma通道完成一次dma传输后的回调函数,也即是s3c2410_dma_enqueue完成后会调用的函数
回调函数应具有一下格式:
typedef void (*s3c2410_dma_cbfn_t)(struct s3c2410_dma_chan *,
void *buf, int size,
enum s3c2410_dma_buffresult result);
buf可以传递一些有用的数据。
回调函数应具有一下格式:
typedef void (*s3c2410_dma_cbfn_t)(struct s3c2410_dma_chan *,
buf可以传递一些有用的数据。
source: S3C2410_DMASRC_HW(外设),或者S3C2410_DMASRC_MEM(内存)。
hwcfg:
the value for xxxSTCn register,
bit 0: 0=increment pointer, 1=leave pointer
bit 1: 0=soucre is AHB, 1=soucre is APB
devaddr:source 的物理地址。
建立一致性DMA映射函数,
该函数实际获得两个地址,
1、函数的返回值是一个(rc),代表缓冲区的内核虚拟地址
2、相关的总线地址,保存在dma_handle中
发起一次dma传输
参数意义:
* id
the device driver's id information for this buffer
* data
the physical address of the buffer data
* size
the size of the buffer in bytes
将dma_alloc_coherent中得到的 dmaphys传递给s3c2410_dma_enqueue. s3c2410_dma_enqueue提交一次dma请求,当dma通道可用的时候通过s3c2410_dma_loadbuffer开始一次传输,传输完成后会产生irq中断。其dma的中断服务函数中会继续启动dma请求队列中的请求,传输剩下的数据。
将dma_alloc_coherent中得到的 dmaphys传递给s3c2410_dma_enqueue. s3c2410_dma_enqueue提交一次dma请求,当dma通道可用的时候通过s3c2410_dma_loadbuffer开始一次传输,传输完成后会产生irq中断。其dma的中断服务函数中会继续启动dma请求队列中的请求,传输剩下的数据。
DMA的使用有了相关的认识。那么关于DMA的体系架构是怎么建立起来的。接下了看看:
DMA先有arch_initcall(s3c2440_dma_init);core_initcall(s3c24xx_dma_sysclass_init);然后late_initcall(s3c24xx_dma_sysdev_register);
先看看段arch_initcall(s3c2440_dam_init)做了些什么。
它主要是驱动注册
static int __init s3c2440_dma_init(void)
{
return sysdev_driver_register(&s3c2440_sysclass, &s3c2440_dma_driver);
}
{
}
&s3c2440_sysclass,s3c2440_dma_driver定义如下
struct sysdev_class s3c2440_sysclass = {
.name
= "s3c2440-core",
.suspend
= s3c244x_suspend,
.resume
= s3c244x_resume
};
};
static struct sysdev_driver s3c2440_dma_driver = {
.add
= s3c2440_dma_add,
};
这里我们只关心.add=s3c2440_dam_add
};
这里我们只关心.add=s3c2440_dam_add
在函数里s3c2440_dam_add做了什么呢?
static int __init s3c2440_dma_add(struct sys_device *sysdev)
{
s3c2410_dma_init();
s3c24xx_dma_order_set(&s3c2440_dma_order);
return s3c24xx_dma_init_map(&s3c2440_dma_sel);
}
{
}
它调用了三个函数s3c2410_dma_init(),s3c24xx_dma_order_set(&s3c2440_dma_order),s3c24xx_dma_init_map(&s3c2440_dma_sel);
s3c2410_dma_init()完成s3c2410_chans[]的初始化,并完成地址映射。s3c2410_chans[]是s3c2410_dma_chan结构体类型数组。s3c2410_dma_chan描述了一个通道的属性,下面是这个结构体得定义:
struct s3c2410_dma_chan {
unsigned char
number;
代表第几个DMA通道
unsigned char
in_use;
是否使用,0:为被使用1:已被占用
unsigned char
irq_claimed;
unsigned char
irq_enabled;
//irq相关标识,用于判断
unsigned char
xfer_unit;
一次传输大小
};
下面是s3c2410_dma_init()函数的源代码:
int __init s3c2410_dma_init(void)
{
return s3c24xx_dma_init(4, IRQ_DMA0, 0x40);
}
int __init s3c24xx_dma_init(unsigned int channels, unsigned int irq,
unsigned int stride)
{
struct s3c2410_dma_chan *cp;
int channel;
int ret;
{
}
int __init s3c24xx_dma_init(unsigned int channels, unsigned int irq,
{
}
有几个全局变量需要注意:
static void __iomem *dma_base;
static struct kmem_cache *dma_kmem;
static int dma_channels;通道数
struct s3c2410_dma_chan s3c2410_chans[S3C_DMA_CHANNELS];
接下来倒回到s3c24xx_dma_order_set(&s3c2440_dma_order)预定目标板要用的DMA通道。
&s3c2440_dma_order定义如下:
struct s3c24xx_dma_order {
struct s3c24xx_dma_order_ch
channels[DMACH_MAX];
};
};
static struct s3c24xx_dma_order __initdata s3c2440_dma_order = {
.channels
= {
[DMACH_SDI]
= {
.list
= {
[0]
= 3 | DMA_CH_VALID,
[1]
= 2 | DMA_CH_VALID,
[2]
= 1 | DMA_CH_VALID,
[3]
= 0 | DMA_CH_VALID,
},
},
[DMACH_I2S_IN]
= {
.list
= {
[0]
= 1 | DMA_CH_VALID,
[1]
= 2 | DMA_CH_VALID,
},
},
[DMACH_I2S_OUT]
= {
.list
= {
[0]
= 2 | DMA_CH_VALID,
[1]
= 1 | DMA_CH_VALID,
},
},
[DMACH_PCM_IN] = {
.list
= {
[0]
= 2 | DMA_CH_VALID,
[1]
= 1 | DMA_CH_VALID,
},
},
[DMACH_PCM_OUT] = {
.list
= {
[0]
= 1 | DMA_CH_VALID,
[1]
= 3 | DMA_CH_VALID,
},
},
[DMACH_MIC_IN] = {
.list
= {
[0]
= 3 | DMA_CH_VALID,
[1]
= 2 | DMA_CH_VALID,
},
},
},
};
};
int __init s3c24xx_dma_order_set(struct s3c24xx_dma_order *ord)
{
struct s3c24xx_dma_order *nord = dma_order;
{
}
dam_order是一个全局变量
static struct s3c24xx_dma_order *dma_order;函数中使用kmalloc获得nord,然后memcpy s3c2440_dma_order 给nord。这里就有两个值相同的内存块。而dma_order指针指向nord.完成全局变量赋值。
s3c24xx_dma_init_map(&s3c2440_dma_sel);
看这个函数想看下相关的东西
static struct s3c24xx_dma_selection __initdata s3c2440_dma_sel = {
.select
= s3c2440_dma_select,
.dcon_mask
= 7 << 24,
.map
= s3c2440_dma_mappings,
.map_size
= ARRAY_SIZE(s3c2440_dma_mappings),
};
static struct s3c24xx_dma_map __initdata s3c2440_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[1]
= S3C2440_DCON_CH1_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[0]
= S3C2440_DCON_CH0_I2SSDO | DMA_CH_VALID,
.channels[2]
= S3C2410_DCON_CH2_I2SSDO | DMA_CH_VALID,
.hw_addr.to
= S3C2410_PA_IIS + S3C2410_IISFIFO,
},
[DMACH_PCM_IN] = {
.name
= "pcm-in",
.channels[0]
= S3C2440_DCON_CH0_PCMIN | DMA_CH_VALID,
.channels[2]
= S3C2440_DCON_CH2_PCMIN | DMA_CH_VALID,
.hw_addr.from
= S3C2440_PA_AC97 + S3C_AC97_PCM_DATA,
},
[DMACH_PCM_OUT] = {
.name
= "pcm-out",
.channels[1]
= S3C2440_DCON_CH1_PCMOUT | DMA_CH_VALID,
.channels[3]
= S3C2440_DCON_CH3_PCMOUT | DMA_CH_VALID,
.hw_addr.to
= S3C2440_PA_AC97 + S3C_AC97_PCM_DATA,
},
[DMACH_MIC_IN] = {
.name
= "mic-in",
.channels[2]
= S3C2440_DCON_CH2_MICIN | DMA_CH_VALID,
.channels[3]
= S3C2440_DCON_CH3_MICIN | DMA_CH_VALID,
.hw_addr.from
= S3C2440_PA_AC97 + S3C_AC97_MIC_DATA,
},
[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,
},
};
};
static struct s3c24xx_dma_map __initdata s3c2440_dma_mappings[] = {
};
s3c2440_dma_sel实现对S3C24XX下DMA资源的统一管理。
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;//dcon控制器掩码
};
struct s3c24xx_dma_map {
const char
*name;//虚拟通道名
struct s3c24xx_dma_addr
hw_addr;
};
struct s3c24xx_dma_map提供了DAM虚拟通道与实际的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;
{
}
函数初始化了全局变量static struct s3c24xx_dma_selection dma_sel;
s3c24xx_dma_check_entry()是空函数。
plat-s3c24xx的dma.c中封装了一系列的API。以后慢慢看。