分类: 嵌入式
2013-06-05 14:23:15
原文地址:DMA原理 作者:qinglajiao
对这些DMA通道和其对应DMA源的管理在文件linux/arch/arm/mach-s3c2440/dma.c中实现。
在文件dma.c中定义了一个结构体数组static struct s3c24xx_dma_map __initdata s3c2440_dma_mappings[],
这个结构体将所有DMA源和每个DMA源所能请求的DMA通道联系了起来。
这个管理结构体原型如下:
struct s3c24xx_dma_map {
const char *name; //DMA源的名
struct s3c24xx_dma_addr hw_addr; //源的物理地址。
unsigned long channels[S3C2410_DMA_CHANNELS]; //在S3C2440中S3C2410_DMA_CHANNELS为4,。
unsigned long channels_rx[S3C2410_DMA_CHANNELS];
};
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", //DMA源为SD卡 ,该源可以对四个DMA通道发出DMA请求。
.channels[0] = S3C2410_DCON_CH0_SDI | DMA_CH_VALID, // DMA_CH_VALID表明将该通道初始化为可用。
.channels[1] = S3C2440_DCON_CH1_SDI | DMA_CH_VALID,
//S3C2440_DCON_CH2_SDI将被写入DMA控制寄存器,用于DMA通道请求源选择。
.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,
},
};
/*************************************************************************************************************************/
//当某DMA通道被使用时调用该函数,置该DMA通道被占用标识。
//获取该DMA源对应的DMA通道请求源选择,存于chan->dcon中最终将被写入DMA控制寄存器。
static void s3c2440_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_selection __initdata s3c2440_dma_sel = {
.select = s3c2440_dma_select, //通道选择函数
.dcon_mask = 7 << 24, //屏蔽DMA控制寄存器中用于设置DMA请求源的位。
.map = s3c2440_dma_mappings, //上面初始化的 源-----通道 管理结构体数组
.map_size = ARRAY_SIZE(s3c2440_dma_mappings), //源的数目。
};
//有一些DMA源不只对应一个DMA通道,将这些源提了出来单独用一个结构体来管理。
//DMA源DMACH_TIMER也对应多个DMA通道,但没有出现在下面结构体中,因为
//定时器本身并不进行数据传输,而其他DMA源都是在数据传输时请求一个DMA通道进行,
//源的寄存器与内存的数据传输。定时器是在DMA进行传输时让它来定时触发传输操作。
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,
},
},
},
};
//为把DMA注册到内核定义了一个s3c2440_dma_driver。
static struct sysdev_driver s3c2440_dma_driver = {
.add = s3c2440_dma_add,
};
//函数s3c2440_dma_init()即是完成了这个注册的工作。
static int __init s3c2440_dma_init(void)
{
return sysdev_driver_register(&s3c2440_sysclass, &s3c2440_dma_driver);
}
//这个注册过程最重要的工作就是调用了函数s3c2440_dma_driver->add,即是函数s3c2440_dma_add()。
这个函数的实现仍然是在文件linux/arch/arm/mach-s3c2440/dma.c中。
static int __init s3c2440_dma_add(struct sys_device *sysdev)
{
s3c2410_dma_init(); (1)
s3c24xx_dma_order_set(&s3c2440_dma_order); (2)
return s3c24xx_dma_init_map(&s3c2440_dma_sel); (3)
}
/*
函数s3c2440_dma_add()的工作就是将文件linux/arch/arm/mach-s3c2440/dma.c中初始化的
一些量拷到文件linux/arch/arm/plat-s3c24xx/dma.c中,或初始化文件linux/arch/arm/plat-s3c24xx/dma.c中
的结构体。文件linux/arch/arm/plat-s3c24xx/dma.c中定义了一些通用的DMA操作函数,这些函数最终是对
一个具体平台的硬件的操作。
*/
(1)
int __init s3c2410_dma_init(void)
{//4个DMA通道,起始中断号IRQ_DMA0,每一个DMA通道的寄存器覆盖地址范围为0x40。
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;
printk("S3C24XX DMA Driver, (c) 2003-2004,2006 Simtec Electronics\n");
//本函数的实现是在文件linux/arch/arm/plat-s3c24xx/dma.c中,这个文件中还实现了其他DMA操作函数。
//dma_channels是在该文件中的全局变量,在其他DMA操作函数中要用这个变量。
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;
}
//创建一个内存池,在创建内存管理结构体s3c2410_dma_buf时便是从该池中获取这些结构体内存。
//DMA的操作是外设与内存的数据传输,这些内存就是用结构体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++) {
//该结构体数组在文件linux/arch/arm/plat-s3c24xx/dma.c中定义,对于S3C2440这个结构体又四个元素,
//每个元素代表一个DMA通道。这个循环是要初始化每一个通道的中断号,通道寄存器基地址等字段。
cp = &s3c2410_chans[channel] ;//
memset(cp, 0, sizeof(struct s3c2410_dma_chan));
/* dma channel irqs are in order.. */
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;
}
(2)
//该函数是将文件linux/arch/arm/mach-s3c2440/dma.c中初始化的结构体s3c2440_dma_order。
//拷到文件linux/arch/arm/plat-s3c24xx/dma.c中来,并用指针dma_order指向。
//在文件linux/arch/arm/plat-s3c24xx/dma.c中实现了一些通用的DMA操作函数,这些函数
//将会有对s3c2440_dma_order的操作。
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;
}
(3)
//该函数是将文件linux/arch/arm/mach-s3c2440/dma.c中初始化的结构体s3c2440_dma_sel
//拷到文件linux/arch/arm/plat-s3c24xx/dma.c中来。
//在文件linux/arch/arm/plat-s3c24xx/dma.c中实现了一些通用的DMA操作函数,这些函数
//将会有对s3c2440_dma_sel的操作。
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;
m