您的位置:首页 > 运维架构 > Linux

Linux内核开发之内存与I/O访问(五)

2012-12-11 17:46 363 查看
“小王,告诉你一个好消息,最难理解的部分不知不觉中已经讲完了,今天的课程就简单多了,而且最重要的是咱们的Linux设备驱动核心理论课也差不多了…”
“最难的部分?已经讲完了?我咋没感觉呢..你讲的真是太好了,太通俗易懂了,太..”小王调皮的说。
“切,就你嘴甜,我还不知道你啊,小脑筋..”我白了小王一样。
那么今天呢?今天就讲讲IO内存静态映射。在将Linux移植到目标电路板中,通常会建立外设IO内存物理地址到虚拟地址的静态映射,这个映射通过在电路板对应的
map_desc结构体数组中添加新的成员来完成,map_desc结构体的定义如下:
structmap_desc
{
unsignedlongvirtual;//虚拟地址
unsignedlongpfn;//__phys_to_pfn(phy_addr)
unsignedlonglength;//大小
unsignedinttype;//类型
}

将Linux操作系统移植到特定平台上,MACHINE_START到MACHINE_EDN宏之间的定义针对特定电路板而设计,其中的map_io()成员函数完成IO内存的静态映射,最后通过调用cpu—>map_io()建立map_desc数组中物理内存和虚拟内存的静态映射关系。

在一个已经移植好的OS的内核中,驱动工程师完全可以对非常规内存区域的IO内存(外设控制器寄存器,MCU内部集成的外设控制寄存器等)依照电路板的资源使用情况添加到map_desc数组中。下边给出SMDK2440内存情况定义的map_desc:




SMDK2440内存资源情况定义的map_desc#include<linux/kernel.h>
#include<linux/types.h>
#include<linux/interrupt.h>
#include<linux/list.h>
#include<linux/timer.h>
#include<linux/init.h>
#include<linux/platform_device.h>

#include<asm/mach/arch.h>
#include<asm/mach/map.h>
#include<asm/mach/irq.h>

#include<asm/hardware.h>
#include<asm/hardware/iomd.h>
#include<asm/io.h>
#include<asm/irq.h>
#include<asm/mach-types.h>

//#include<asm/debug-ll.h>
#include<asm/arch/regs-serial.h>
#include<asm/arch/regs-gpio.h>
#include<asm/arch/regs-lcd.h>

#include<asm/arch/idle.h>
#include<asm/arch/fb.h>

#include"s3c2410.h"
#include"s3c2440.h"
#include"clock.h"
#include"devs.h"
#include"cpu.h"
#include"pm.h"

staticstructmap_descsmdk2440_iodesc[]__initdata={
/*ISAIOSpacemap(memoryspaceselectedbyA24)*/

{
.virtual	=(u32)S3C24XX_VA_ISA_WORD,
.pfn		=__phys_to_pfn(S3C2410_CS2),
.length		=0x10000,
.type		=MT_DEVICE,
},{
.virtual	=(u32)S3C24XX_VA_ISA_WORD+0x10000,
.pfn		=__phys_to_pfn(S3C2410_CS2+(1<<24)),
.length		=SZ_4M,
.type		=MT_DEVICE,
},{
.virtual	=(u32)S3C24XX_VA_ISA_BYTE,
.pfn		=__phys_to_pfn(S3C2410_CS2),
.length		=0x10000,
.type		=MT_DEVICE,
},{
.virtual	=(u32)S3C24XX_VA_ISA_BYTE+0x10000,
.pfn		=__phys_to_pfn(S3C2410_CS2+(1<<24)),
.length		=SZ_4M,
.type		=MT_DEVICE,
}
};

#defineUCONS3C2410_UCON_DEFAULT|S3C2410_UCON_UCLK
#defineULCONS3C2410_LCON_CS8|S3C2410_LCON_PNONE|S3C2410_LCON_STOPB
#defineUFCONS3C2410_UFCON_RXTRIG8|S3C2410_UFCON_FIFOMODE

staticstructs3c2410_uartcfgsmdk2440_uartcfgs[]={
[0]={
.hwport	=0,
.flags	=0,
.ucon	=0x3c5,
.ulcon	=0x03,
.ufcon	=0x51,
},
[1]={
.hwport	=1,
.flags	=0,
.ucon	=0x3c5,
.ulcon	=0x03,
.ufcon	=0x51,
},
/*IRport*/
[2]={
.hwport	=2,
.flags	=0,
.ucon	=0x3c5,
.ulcon	=0x43,
.ufcon	=0x51,
}
};

/*LCDdriverinfo*/

staticstructs3c2410fb_mach_infosmdk2440_lcd_cfg__initdata={
.regs	={

.lcdcon1	=S3C2410_LCDCON1_TFT16BPP|
S3C2410_LCDCON1_TFT|
S3C2410_LCDCON1_CLKVAL(0x04),

.lcdcon2	=S3C2410_LCDCON2_VBPD(7)|
S3C2410_LCDCON2_LINEVAL(319)|
S3C2410_LCDCON2_VFPD(6)|
S3C2410_LCDCON2_VSPW(3),

.lcdcon3	=S3C2410_LCDCON3_HBPD(19)|
S3C2410_LCDCON3_HOZVAL(239)|
S3C2410_LCDCON3_HFPD(7),

.lcdcon4	=S3C2410_LCDCON4_MVAL(0)|
S3C2410_LCDCON4_HSPW(3),

.lcdcon5	=S3C2410_LCDCON5_FRM565|
S3C2410_LCDCON5_INVVLINE|
S3C2410_LCDCON5_INVVFRAME|
S3C2410_LCDCON5_PWREN|
S3C2410_LCDCON5_HWSWP,
},

#if0
/*currentlysetupbydownloader*/
.gpccon		=0xaa940659,
.gpccon_mask	=0xffffffff,
.gpcup		=0x0000ffff,
.gpcup_mask	=0xffffffff,
.gpdcon		=0xaa84aaa0,
.gpdcon_mask	=0xffffffff,
.gpdup		=0x0000faff,
.gpdup_mask	=0xffffffff,
#endif

.lpcsel		=((0xCE6)&~7)|1<<4,

.width		=240,
.height		=320,

.xres		={
.min	=240,
.max	=240,
.defval	=240,
},

.yres		={
.min	=320,
.max	=320,
.defval=320,
},

.bpp		={
.min	=16,
.max	=16,
.defval=16,
},
};

staticstructplatform_device*smdk2440_devices[]__initdata={
&s3c_device_usb,
&s3c_device_lcd,
&s3c_device_wdt,
&s3c_device_i2c,
&s3c_device_iis,
};

staticstructs3c24xx_boardsmdk2440_board__initdata={
.devices=smdk2440_devices,
.devices_count=ARRAY_SIZE(smdk2440_devices)
};

staticvoid__initsmdk2440_map_io(void)
{
s3c24xx_init_io(smdk2440_iodesc,ARRAY_SIZE(smdk2440_iodesc));
s3c24xx_init_clocks(16934400);
s3c24xx_init_uarts(smdk2440_uartcfgs,ARRAY_SIZE(smdk2440_uartcfgs));
s3c24xx_set_board(&smdk2440_board);
}

staticvoid__initsmdk2440_machine_init(void)
{
/*ConfiguretheLEDs(evenifwehavenoLEDsupport)*/

s3c2410_gpio_cfgpin(S3C2410_GPF4,S3C2410_GPF4_OUTP);
s3c2410_gpio_cfgpin(S3C2410_GPF5,S3C2410_GPF5_OUTP);
s3c2410_gpio_cfgpin(S3C2410_GPF6,S3C2410_GPF6_OUTP);
s3c2410_gpio_cfgpin(S3C2410_GPF7,S3C2410_GPF7_OUTP);

s3c2410_gpio_setpin(S3C2410_GPF4,0);
s3c2410_gpio_setpin(S3C2410_GPF5,0);
s3c2410_gpio_setpin(S3C2410_GPF6,0);
s3c2410_gpio_setpin(S3C2410_GPF7,0);

s3c24xx_fb_set_platdata(&smdk2440_lcd_cfg);

s3c2410_pm_init();
}

MACHINE_START(S3C2440,"SMDK2440")
/*Maintainer:BenDooks<ben@fluff.org>*/
.phys_io	=S3C2410_PA_UART,
.io_pg_offst	=(((u32)S3C24XX_VA_UART)>>18)&0xfffc,
.boot_params	=S3C2410_SDRAM_PA+0x100,

.init_irq	=s3c24xx_init_irq,
.map_io		=smdk2440_map_io,
.init_machine	=smdk2440_machine_init,
.timer		=&s3c24xx_timer,
MACHINE_END


“小王,今天要讲的另外一个主题是DMA”我补充道:

DMA是一种无须CPU的参与就可以让外设与系统内存之间进行双向数据传输的硬件机制。使用DMA可以是系统CPU从实际的IO数据传输过程中摆脱出来,从而大大提

供系统的吞吐率。DMA方式的数据传输由DMA控制器(DMAC)控制,在传输期间,CPU可以并发地执行其他任务,当DMA结束后,DMAC通过中断通知CPU数据传输已经结束,然后由CPU执行相应的中断服务程序进行后续处理。

说到DMA,就会想到Cache,两者本身似乎是好不相关的事物。的确,假设DMA针对内存的目的地址和Cache缓存的对象没有重叠区域,DMA和Cache之间就相安无事,但是,如果有重叠呢,经过DMA操作,Cache缓存对应的内存的数据已经被修改,而CPU本身并不知道,它仍然认为Cache中的数据仍然还是内存中的数据,以后访问Cache映射的内存时,它仍然使用陈旧的Cache数据,这就会发生Cache与内存之间数据“不一致性”的错误。一旦出现这样的情况,没有处理好,驱动就将无

法正常运行。那么怎样解决呢?最简单的方法是直接禁止DMA目标地址范围内内存的Cache功能,当然这是牺牲性能的,但却高可靠。不是吗,所以这两者之间究竟怎么平衡,还真不好解决。

其实啊,Cache不一致的情况在其他地方也是可能发生的,比如,对于带MMU功能的ARM处理器,在开启MMU之前,需要设置Cache无效,TLB也是如此。

说了,那么多DMA理论的东西,剩下的来点Linux下DMA编程的东西,当然,这里也是点一下,具体怎么操作,我可要卖个关子到下节了。

在内存中用于与外设交互数据的一块区域被称作DMA缓冲区,在设备不支持scatter/gatherCSG,分散/聚集操作的情况下,DMA缓冲区必须是物理上联系的。

对于ISA设备而言,其DMA操作只能在16MB以下的内存进行,因此,在使用kmalloc()和__get_free_pages()及其类似函数申请DMA缓冲区时应使用GFP_DMA标

志,这样能保证获得的内存是具备DMA能力的。

内核中定义了__get_free_pages()针对DMA的“快捷方式”__get_dma_pages(),它在申请标志中添加了GFP_DMA,如下所示:
#define__get_dma__pages(gfp_mask,order)__get_free_pages((gfp_mask)|GFP_DMA,(order))
"我不想使用order为参数的申请DMA内存,感觉就是怪怪的,那咋办?"小王抱怨道.
那?这样吧,你就用另外一个函数dma_mem_alloc()源代码如下:

staticunsignedlongdma_mem_alloc(intsize)
{
intorder=get_order(size);//大小->指数
return__get_dma_pages(GFP_KERNEL,order);
}
"小王,感觉怎样,要不咱们下节继续?看你挺多不懂的地方?"我看到小王那充满疑惑的眼神。
"好,行,我正有这种打算呢"小王又露出了让人陶醉的笑容。




                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: