现在的位置: 首页 > 综合 > 正文

IO空间的静态映射基本过程

2018年08月23日 ⁄ 综合 ⁄ 共 7484字 ⁄ 字号 评论关闭

1、前言

IO端口与IO内存的概念:外设中的寄存器称为是IO端口,外设中的内存称为是IO内存。二者统称为IO空间。Linux内核是通过虚拟地址访问外设的。所以需要先将虚拟地址映射到相应外设的物理地址上,linux的映射方式有两种:静态映射(map_desc)和动态映射(ioremap),其实也是内核访问外设资源的方式。

2、静态映射基本过程:

在驱动中配置寄存器,可以调用类似于s3c_gpio_cfgpin、s3c_gpio_setpull、gpio_direction_output等直接配置IO寄存器的函数。这些函数访问的虚拟地址,这些虚拟地址都是已经在启动启动的时候通过静态映射方式映射到IO寄存器的物理地址上。

静态映射概念:所谓的静态映射是指,虚拟地址到物理地址的转换所需要的页表在操作系统启动时已经配置好,不需要用户进行配置,虚拟地址到物理地址的查表转换可直接完成(fromnet)

以s5pv210为例讲述:

MACHINE_START(SMDKV210,"SMDKV210")

    /* Maintainer: Kukjin Kim<kgene.kim@samsung.com> */

    .boot_params    =S5P_PA_SDRAM + 0x100,

    .init_irq   =s5pv210_init_irq,

    .map_io       = smdkv210_map_io,

    .init_machine   = smdkv210_machine_init,

#ifdefCONFIG_S5P_HIGH_RES_TIMERS

        .timer          = &s5p_systimer,

#else

    .timer      =&s5p_timer,

#endif

MACHINE_END

其中IO映射函数是smdkv210_map_io实现:

staticvoid __init smdkv210_map_io(void)

{

    s5p_init_io(NULL,0, S5P_VA_CHIPID);

    s3c24xx_init_clocks(24000000);

    s5pv210_gpiolib_init();

    s3c24xx_init_uarts(smdkv210_uartcfgs,

ARRAY_SIZE(smdkv210_uartcfgs));

#ifndefCONFIG_S5P_HIGH_RES_TIMERS

    s5p_set_timer_source(S5P_PWM2, S5P_PWM4);

#endif

    s5p_reserve_bootmem(s5pv210_media_devs,

                       ARRAY_SIZE(s5pv210_media_devs),

S5P_RANGE_MFC);

}

    该函数中调用s5p_init_io进行地址的映射,

/*read cpu identification code */

 

void__init s5p_init_io(struct map_desc*mach_desc,

            int size, void __iomem *cpuid_addr)

{

    unsigned long idcode;

 

    /* initialize the io descriptors we need forinitialization */

    iotable_init(s5p_iodesc, ARRAY_SIZE(s5p_iodesc));

    if (mach_desc)

        iotable_init(mach_desc, size);

 

    idcode = __raw_readl(cpuid_addr);

    s3c_init_cpu(idcode,cpu_ids, ARRAY_SIZE(cpu_ids));

}

其中最重要是struct map_desc s5p_iodesc[]结构体 和 staticstruct map_desc s5pv210_iodesc[]:

struct map_desc {

    unsigned long virtual; // IO空间映射后的虚拟地址

    unsigned long pfn;     // IO空间的物理地址所在的页帧号

unsigned long length;  // IO空间的长度

    unsigned int type;     // IO空间的类型

};

/*minimal IO mapping这部分是最小系统层次的映射*/

staticstruct map_desc s5p_iodesc[] __initdata = {

    {

        .virtual    =(unsigned long)S5P_VA_CHIPID,

        .pfn        =__phys_to_pfn(S5P_PA_CHIPID),

        .length     =SZ_4K,

        .type       =MT_DEVICE,

    }, {

        .virtual   = (unsigned long)S3C_VA_SYS,

       .pfn       = __phys_to_pfn(S5P_PA_SYSCON),

       .length       = SZ_64K,

       .type      = MT_DEVICE,

    },{

        .virtual    =(unsigned long)S3C_VA_TIMER,

        .pfn        =__phys_to_pfn(S5P_PA_TIMER),

        .length     =SZ_16K,

        .type       =MT_DEVICE,

    },{

        .virtual    =(unsigned long)S3C_VA_WATCHDOG,

        .pfn        =__phys_to_pfn(S3C_PA_WDT),

        .length     =SZ_4K,

        .type       =MT_DEVICE,

    }, {

        .virtual    =(unsigned long)S5P_VA_SROMC,

        .pfn        =__phys_to_pfn(S5P_PA_SROMC),

        .length     =SZ_4K,

        .type       =MT_DEVICE,

    },

};

/*Initial IO mappings*/

staticstruct map_desc s5pv210_iodesc[] __initdata = {

    {

        .virtual    =(unsigned long)S5P_VA_SYSTIMER,

        .pfn        =__phys_to_pfn(S5PV210_PA_SYSTIMER),

        .length     =SZ_4K,

        .type       =MT_DEVICE,

    }, {

        .virtual    =(unsigned long)S5P_VA_GPIO,

        .pfn        =__phys_to_pfn(S5PV210_PA_GPIO),

        .length     =SZ_4K,

        .type       =MT_DEVICE,

    }, {

        .virtual    =(unsigned long)VA_VIC0,

        .pfn        =__phys_to_pfn(S5PV210_PA_VIC0),

        .length     =SZ_16K,

        .type       =MT_DEVICE,

    }, {

        .virtual    =(unsigned long)VA_VIC1,

        .pfn        =__phys_to_pfn(S5PV210_PA_VIC1),

        .length     =SZ_16K,

        .type       =MT_DEVICE,

    }, {

        .virtual    =(unsigned long)VA_VIC2,

        .pfn        =__phys_to_pfn(S5PV210_PA_VIC2),

        .length     =SZ_16K,

        .type       =MT_DEVICE,

    }, {

        .virtual    =(unsigned long)VA_VIC3,

        .pfn        =__phys_to_pfn(S5PV210_PA_VIC3),

        .length     =SZ_16K,

        .type       =MT_DEVICE,

    }, {

        .virtual    =(unsigned long)S3C_VA_UART,

        .pfn        =__phys_to_pfn(S3C_PA_UART),

        .length     =SZ_512K,

        .type       =MT_DEVICE,

    }, {

        .virtual    =(unsigned long)S5P_VA_DMC0,

        .pfn        =__phys_to_pfn(S5PV210_PA_DMC0),

        .length     =SZ_4K,

        .type       =MT_DEVICE,

    }, {

        .virtual    =(unsigned long)S5P_VA_DMC1,

        .pfn        =__phys_to_pfn(S5PV210_PA_DMC1),

        .length     =SZ_4K,

        .type       =MT_DEVICE,

    },{

       .virtual        = (unsigned long)S5P_VA_BUS_AXI_DSYS,

       .pfn            = __phys_to_pfn(S5PV210_PA_BUS_AXI_DSYS),

       .length        = SZ_4K,

       .type           = MT_DEVICE,

        }, {

       .virtual        = (unsigned long)S5P_VA_BUS_AXI_VSYS,

       .pfn            = __phys_to_pfn(S5PV210_PA_BUS_AXI_VSYS),

       .length         = SZ_4K,

       .type           = MT_DEVICE,

        }, {

        .virtual        = (unsigned long)S5P_VA_BUS_AXI_XSYS,

        .pfn           = __phys_to_pfn(S5PV210_PA_BUS_AXI_XSYS),

        .length         = SZ_4K,

        .type           = MT_DEVICE,

        }, {

        .virtual    =(unsigned long)S3C_VA_USB_HSPHY,

        .pfn        =__phys_to_pfn(S5PV210_PA_HSPHY),

        .length     =SZ_4K,

        .type       =MT_DEVICE,

    }, {

        .virtual    =(unsigned long)S3C_VA_OTG,

        .pfn        =__phys_to_pfn(S5PV210_PA_OTG),

        .length     =SZ_1M,

        .type       =MT_DEVICE,

    }, {

        .virtual    =(unsigned long)S3C_VA_OTGSFR,

        .pfn        =__phys_to_pfn(S5PV210_PA_OTGSFR),

        .length     =SZ_1M,

        .type       =MT_DEVICE,

    },

#ifdefined(CONFIG_HRT_RTC)

        {

         .virtual        = (unsigned long)S5P_VA_RTC,

         .pfn           = __phys_to_pfn(S5PV210_PA_RTC),

         .length         = SZ_4K,

         .type          = MT_DEVICE,

        },

#endif

       {

        .virtual        = (unsigned long)S5P_VA_AUDSS,

        .pfn            = __phys_to_pfn(S5PV210_PA_AUDSS),

        .length         = SZ_1M,

        .type           = MT_DEVICE,

        },

 

};

说明:

虚拟地址的定义(只截取了部分为例):

#defineS3C_ADDR_BASE   (0xFD000000)

 

#ifndef__ASSEMBLY__

#defineS3C_ADDR(x) ((void __iomem __force*)S3C_ADDR_BASE + (x))

#else

#defineS3C_ADDR(x) (S3C_ADDR_BASE + (x))

#endif

 

#defineS3C_VA_IRQ  S3C_ADDR(0x00000000)    /* irq controller(s) */

#define S3C_VA_SYS   S3C_ADDR(0x00100000) /*system control */

#defineS3C_VA_MEM  S3C_ADDR(0x00200000)    /* memory control */

……

#defineS3C_ADDR_CPU(x) S3C_ADDR(0x00500000 + (x))

#endif/* __ASM_PLAT_MAP_H */

 

物理地址的定义(只截取了部分为例):

……

#defineS5P_PA_SDRAM            S5PV210_PA_SDRAM

#defineS5P_PA_SROMC            S5PV210_PA_SROMC

#define S5P_PA_SYSCON          S5PV210_PA_SYSCON

#defineS5P_PA_TIMER            S5PV210_PA_TIMER

……

////////////////////////////////////////////////////////////

#define S5P_VA_GPIO      S3C_ADDR(0x01500000)

////////////////////////////////////////////////////////////

……

#defineS5PV210_PA_CHIPID       0xE0000000

 

#define S5PV210_PA_SYSCON       0xE0100000

 

#define S5PV210_PA_GPIO         0xE0200000

……

#defineS5PV210_PA_TIMER        0xE2500000

#defineS5PV210_PA_SYSTIMER     0xE2600000

#defineS5PV210_PA_WATCHDOG     0xE2700000

#defineS5PV210_PA_RTC          0xE2800000

……

比如物理地址为E0100000 SYSCON部分就被映射到了S3C_ADDR(0x00100000)也就是0xFD100000的虚拟地址上。

再比如物理地址为E0200000 GPIO部分就被映射到了S3C_ADDR(0x01500000)也就是0XFE500000的虚拟地址上。

 

第二个结构体数组的映射实现是通过s3c_init_cpu(idcode,cpu_ids, ARRAY_SIZE(cpu_ids))函数调用s5pv210_map_io实现:

static struct cpu_table cpu_ids[]__initdata = {

{

        .idcode     =0x43110000,

        .idmask     =0xfffff000,

        .map_io       = s5pv210_map_io,

        .init_clocks    = s5pv210_init_clocks,

        .init_uarts = s5pv210_init_uarts,

        .init       =s5pv210_init,

        .name       =name_s5pv210,

    },

 

void__init s5pv210_map_io(void)

{

    iotable_init(s5pv210_iodesc, ARRAY_SIZE(s5pv210_iodesc));

    ……

}

iotable_init涉及了内存管理,实现代码:

void__init iotable_init(struct map_desc *io_desc, int nr)

{

    int i;

    for (i = 0; i < nr; i++)

        create_mapping(io_desc + i);

}

到此,基本上就完成了s5p_iodesc和 s5pv210_iodesc的映射。从此刻开始内核可以通过IO空间映射后的虚拟地址访问外设IO空间。

抱歉!评论已关闭.