Chinaunix首页 | 论坛 | 博客
  • 博客访问: 121654
  • 博文数量: 31
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 296
  • 用 户 组: 普通用户
  • 注册时间: 2015-01-10 21:57
文章分类

全部博文(31)

文章存档

2016年(4)

2015年(27)

我的朋友

分类: LINUX

2015-03-19 14:31:41

在内核访问设备之前,首先需要将设备所处的物理地址映射到虚拟地址空间。我们一般使用两种方式。一种是动态映射的方式,利用ioremap函数进行映射, iounmap进行释放来完成的;一种是静态映射的方式。内存静态映射分3个层次:

开发板的层次(如:声卡,网卡和开发板相关的部分) 

最小系统的层次(系统必需的几个,如GPIO,IRQ,MEMCTRL,UART) 

其他系统的层次(不影响开机的部分,如:usblcdadc

setup_arch函数中会调用paging_init函数,调用形式如下:

paging_init(&meminfo, mdesc)

其中meminfo中存放内存信息,正如前面所讲到的,U-Boot tag列表通过在内存标记将内存信息传给内核,在start_kernel中调用setup_arch函数,setup_arch函数会调用setup_archparse_tag_mem32()函数对内存标记进行处理,根据内存标记定义内存的起始地址、长度,在全局变量meminfo中增加内存的描述信息。以后内核就可以通过meminfo结构了解开发板的内存信息。

mdesc就是前面lookup_machine_type函数返回的machine_dec的结构。对于s3c2410来说,这个结构在arch/arm/mach-s3c2410/mach-smdk2410.c中(对于s3c6410来说,这个结构在arch/arm/mach-s3c6410/mach-smdk6410.c中)。代码如下:
/*向系统注册一个machine_desc的对象*/
MACHINE_START(SMDK2410, "SMDK2410") /* @TODO: request a new identifier and switch
                    * to SMDK2410 */
    /* Maintainer: Jonas Dietsche */

/* Maintainer: Samsung Electronics */

.phys_io    = S3C2410_PA_UART,

.io_pg_offst    = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,

.boot_params    = S3C_SDRAM_PA + 0x100,

/*init_irq在start_kernel() --> init_IRQ() --> init_arch_irq() 被调用*/

.init_irq   = s3c24xx_init_irq,

/*map_io 在setup_arch() --> paging_init() --> devicemaps_init()*/

        .map_io     = smdk2410_map_io,  

/*.init_machine 在 arch/arm/kernel/setup.c 中被 customize_machine 调用,放在 arch_initcall() 段里面,会自动按顺序被调用 start_kernel,参考 init/main.c*/
    .init_machine   = smdk2410_init,
    .timer      = &s3c24xx_timer,

MACHINE_END

mach-smdk2410.c中先通过MACHINE_START()定义了machine_desc的变量,其中注册了smdk2410_map_io(), s3c2410_init_irq(), smdk2410_init()3个回调函数3个回调函数会在系统起来的时候setup_arch()里面逐个调用来进行开发板虚实地址映射中断初始化, clock初始化,片上设备的注册等操作,后面会逐一讲到。

paging_init函数在arch/arm/mm/mmu.c中定义,在这个函数它会调用同文件中的函数devicemaps_init(),而在devicemaps_init()中会调用mdesc->map_io函数,对于s3c2410就是调用smdk2410_map_io,而smdk2410_map_io-> s3c24xx_init_io(smdk2410_iodesc, ARRAY_SIZE(smdk2410_iodesc)),s3c24xx_init_io函数在arch/arm/plat-s3c24xx/cpu.c中实现,代码如下:

void __init s3c24xx_init_io(struct map_desc *mach_desc, int size)

{

unsigned long idcode = 0x0;

/* initialise the io descriptors we need for initialisation */

iotable_init(mach_desc, size); //针开发板的层次

iotable_init(s3c_iodesc, ARRAY_SIZE(s3c_iodesc));// 最小系统的层次

if (cpu_architecture() >= CPU_ARCH_ARMv5) {

idcode = s3c24xx_read_idcode_v5();

} else {

idcode = s3c24xx_read_idcode_v4();

}

arm_pm_restart = s3c24xx_pm_restart;

s3c_init_cpu(idcode, cpu_ids, ARRAY_SIZE(cpu_ids)); /*其他系统的层次*/

}

1、 最小系统层次的内存静态映射(与CPU相关)

iotable_init内核提供,定义如下:

/* Create the architecture specific mappings*/
        void __init iotable_init(struct map_desc *io_desc, int nr)
        {
                int i;
                for (i = 0; i nr; i++)
                create_mapping(io_desc + i);
        }

由上知道,smdk2410_map_io最终调用iotable_init建立映射表。

iotable_init函数的参数有两个:一个是map_desc类型的结构体,另一个是该结构体的数量nr。这里最关键的就是struct map_descmap_desc结构体定义如下:

/* include/asm-arm/mach/map.h */
                struct map_desc {
                unsigned long virtual; /* 映射后的虚拟地址 */
                unsigned long physical;/* I/O资源物理地址所在的页帧号*/
                unsigned long length; /* I/O资源长度*/
                unsigned int type; /* I/O资源类型 */
        };

create_mapping( )函数就是通过map_desc提供的信息创建线性映射表的。 

这样的话我们就知道了创建I/O映射表的大致流程为:只要定义相应I/O资源的map_desc结构体,并将该结构体传给iotable_init函数执行,就可以创建相应的I/O资源到内核虚拟地址空间的映射表了。

我们来看看s3c2410是怎么定义map_desc结构体的(即上面iotable_init()函数内的s3c_iodesc)在arch/arm/mach-s3c2410/cpu.c结构体中:
        /* minimal IO mapping */
        static struct map_desc s3c_iodesc[] __initdata = {
                IODESC_ENT(GPIO),
                IODESC_ENT(IRQ),
                IODESC_ENT(MEMCTRL),
                IODESC_ENT(UART)
        };

IODESC_ENT宏如下:

#define IODESC_ENT(x) { (unsigned long)S3C24XX_VA_##x, S3C2410_PA_##x, S3C24XX_SZ_##x, MT_DEVICE }

展开后等价于:

static struct map_desc s3c_iodesc[] __initdata = {
                {
                        .virtual = S3C24XX_VA_GPIO,
                        .physical = S3C24XX_PA_GPIO,
                        .length = S3C24XX_SZ_GPIO,
                        .type = MT_DEVICE
                },
                ……
        };

这里,我们可以比较清晰看到GPIO被静态映射的过程。由于我们在前面的静态映射中已经做好了GPIO的映射,也就是我们写GPIO相关驱动的时候可以不需要映射直接。如下配置引脚的原因,例如s3c2410_gpio_cfgpinxxx,xxx);

其实,s3c2410_gpio_cfgpin定义如下:

void s3c2410_gpio_cfgpin(unsigned int pin, unsigned int function)
        {
                void __iomem *base = S3C2410_GPIO_BASE(pin);
                unsigned long mask;
                unsigned long con;
                unsigned long flags;

        if (pin < S3C2410_GPIO_BANKB) {
                        mask = 1 << S3C2410_GPIO_OFFSET(pin);
                } else {
                        mask = 3 << S3C2410_GPIO_OFFSET(pin)*2;
                }

        local_irq_save(flags);

        con = __raw_readl(base + 0x00);
                con &= ~mask;
                con |= function;

        __raw_writel(con, base + 0x00);

        local_irq_restore(flags);
        }

其中,比较关键的一个地方:
        void __iomem *base = S3C2410_GPIO_BASE(pin);
        这一行中,S3C2410_GPIO_BASE定义如下:

#define S3C2410_GPIO_BASE(pin) ((((pin) & ~31) >> 1) + S3C24XX_VA_GPIO)

至此,GPIO的静态映射就看得很明白了。

2  开发板的层次的静态映射

开发板的层次的静态映射和最小系统层次的静态映射相同,都是通过iotable_init函数来实现的。只是传递的参数值不相同。最小系统层次iotable_init()函数map_desc结构体参数为s3c_iodesc,定义在arch/arm/mach-s3c2410/cpu.c文件中。开发板的层次iotable_init()函数map_desc结构体参数为mach_desc,,定义在arch/arm/mach-s3c2410/mach_smdk2410.c文件中。定义如下:

  static struct map_desc smdk2410_iodesc[] __initdata = {
/*自己添加*/
  };

3  其他外设的静态映射(其他系统的层次)

s3c24xx_init_io()函数中,除了iotable_init()以外,还会在最后调用s3c_init_cpu函数,该函数会调用cpu->map_io()函数。

CPU的这个map_ioarch/arm/mach-s3c2410/cpu.c里面定义如下:

static struct cpu_table cpu_ids[] __initdata = {
                {
                        .idcode = 0x32410000,
                        .idmask = 0xffffffff,
                        .map_io = s3c2410_map_io,
                        .init_clocks = s3c2410_init_clocks, 
                        .init_uarts = s3c2410_init_uarts,
                        .init = s3c2410_init,
                        .name = name_s3c2410
                },
                        ...
         }

s3c2410_map_io -> s3c24xx_init_io -> s3c2410_map_io

再查看s3c2410_map_io(),函数代码如下:

void __init s3c2410_map_io(void)

{

s3c24xx_gpiocfg_default.set_pull = s3c_gpio_setpull_1up;

s3c24xx_gpiocfg_default.get_pull = s3c_gpio_getpull_1up;

iotable_init(s3c2410_iodesc, ARRAY_SIZE(s3c2410_iodesc));//*其他系统的层次*/

}

接下来看结构s3c2410_iodesc [arch/arm/mach-s3c2410/s3c2410.c],代码如下,

/* Initial IO mappings */
        static struct map_desc s3c2410_iodesc[] __initdata = {
                IODESC_ENT(USBHOST),
                IODESC_ENT(USBDEV),
                IODESC_ENT(CLKPWR),
                IODESC_ENT(LCD),
                IODESC_ENT(TIMER),
                IODESC_ENT(ADC),
                IODESC_ENT(WATCHDOG),
        };

赫然发现IODESC_ENT(TIMER)这一行,结合之前GPIO的类似分析,IODESC_ENT宏如下:
        #define IODESC_ENT(x) { (unsigned long)S3C24XX_VA_##x, S3C2410_PA_##x, S3C24XX_SZ_##x, MT_DEVICE }

至此,TIMER, USBHOST,USBDEV,lcd,adc,watchdog等的静态映射都看得很明白了。
S3C24XX_VA_CLKPWR  -->__phys_to_pfn(S3C24XX_PA_CLKPWR),   size 为 

S3C24XX_SZ_CLKPWR
S3C24XX_VA_LCD  --> __phys_to_pfn(S3C24XX_PA_LCD), size 为 S3C24XX_SZ_LCD
S3C24XX_VA_TIMER  --> __phys_to_pfn(S3C24XX_PA_ TIMER), size 为 S3C24XX_SZ_ TIMER
S3C24XX_VA_WATCHDOG  --> __phys_to_pfn(S3C24XX_PA_ WATCHDOG), size 为 S3C24XX_SZ_ WATCHDOG

如果我们要加入我们自己的映射,可以在smdk2410_iodesc[]和s3c2410_iodesc[]添加。以后如果某个资源没有映射也可以在相应的驱动种用ioremap()来动态映射(很多驱动都是这么做的)

注:经静态映射后,在设备驱动中访问的I/O内存时,直接在map_desc中该段的虚拟地址上加上相对偏移即可,不需要使用ioremap

阅读(1714) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~