返回列表 发帖

[站长原创] 移植内核必备知识-6410的寄存器物理虚拟-地址映射方法

[站长原创] 移植内核必备知识-6410的寄存器物理虚拟-地址映射方法

移植内核必备知识-6410的寄存器物理虚拟-地址映射方法

嵌入式开发联盟www.mcuos.com

Osboy站长原创

QQ:82475491

Mcuos.com@gmail.com

(一)6410设备的物理地址与虚拟地址的映射

MACHINE_START(MCUOS6410, "MCUOS6410")

       /* Maintainer: Ben Dooks <ben-linux@fluff.org> */

       .boot_params  = S3C64XX_PA_SDRAM + 0x100,

       .init_irq   = s3c6410_init_irq,


.map_io         = mcuos6410_map_io,


.init_machine  = mcuos6410_machine_init,

       .timer             = &s3c24xx_timer,

MACHINE_END

这里面的mao_io主要工作之一就是做6410的寄存器的物理地址与虚拟地址映射的工作。mao_io的代码为:

static void __init mcuos6410_map_io(void)

{

       s3c64xx_init_io(mcuos6410_iodesc, ARRAY_SIZE(mcuos6410_iodesc));

       s3c24xx_init_clocks(12000000);

       s3c24xx_init_uarts(mcuos6410_uartcfgs, ARRAY_SIZE(mcuos6410_uartcfgs));

}

s3c64xx_init_io最终会调用下面这个函数:

iotable_init(s3c_iodesc, ARRAY_SIZE(s3c_iodesc));

s3c_iodesc结构体定义在:

static struct map_desc s3c_iodesc[] __initdata = {

       {

              .virtual   = (unsigned long)S3C_VA_SYS,

              .pfn        = __phys_to_pfn(S3C64XX_PA_SYSCON),

              .length          = SZ_4K,

              .type              = MT_DEVICE,

       }, {

              .virtual    = (unsigned long)S3C_VA_MEM,

              .pfn        = __phys_to_pfn(S3C64XX_PA_SROM),

              .length            = SZ_4K,

              .type              = MT_DEVICE,

       }, {

              .virtual    = (unsigned long)(S3C_VA_UART + UART_OFFS),

              .pfn        = __phys_to_pfn(S3C_PA_UART),

              .length            = SZ_4K,

              .type              = MT_DEVICE,

       }, {

              .virtual    = (unsigned long)VA_VIC0,

              .pfn        = __phys_to_pfn(S3C64XX_PA_VIC0),

              .length            = SZ_16K,

              .type              = MT_DEVICE,

       }, {

              .virtual    = (unsigned long)VA_VIC1,

              .pfn        = __phys_to_pfn(S3C64XX_PA_VIC1),

              .length            = SZ_16K,

              .type              = MT_DEVICE,

       }, {

              .virtual    = (unsigned long)S3C_VA_TIMER,

              .pfn        = __phys_to_pfn(S3C_PA_TIMER),

              .length            = SZ_16K,

              .type              = MT_DEVICE,

       }, {

              .virtual    = (unsigned long)S3C64XX_VA_GPIO,

              .pfn        = __phys_to_pfn(S3C64XX_PA_GPIO),

              .length            = SZ_4K,

              .type              = MT_DEVICE,

       }, {

              .virtual    = (unsigned long)S3C64XX_VA_MODEM,

              .pfn        = __phys_to_pfn(S3C64XX_PA_MODEM),

              .length            = SZ_4K,

              .type              = MT_DEVICE,

       }, {

              .virtual    = (unsigned long)S3C_VA_WATCHDOG,

              .pfn        = __phys_to_pfn(S3C64XX_PA_WATCHDOG),

              .length            = SZ_4K,

              .type              = MT_DEVICE,

       }, {

              .virtual    = (unsigned long)S3C_VA_USB_HSPHY,

              .pfn        = __phys_to_pfn(S3C64XX_PA_USB_HSPHY),

              .length            = SZ_1K,

              .type              = MT_DEVICE,

       },

};

linux/include/asm-arm/plat-s3c/map.h中有如下定义,我们在这边手动的定义了寄存器的虚拟地址空间:

#define S3C_ADDR_BASE 0xF6000000

#ifndef __ASSEMBLY__

#define S3C_ADDR(x)  ((void __iomem __force *)S3C_ADDR_BASE + (x))

#else

#define S3C_ADDR(x)  (S3C_ADDR_BASE + (x))

#endif

#define S3C_VA_IRQ    S3C_ADDR(0x00000000)     /* irq controller(s) */

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

#define S3C_VA_MEM  S3C_ADDR(0x00200000)     /* memory control */

#define S3C_VA_TIMER       S3C_ADDR(0x00300000)     /* timer block */

#define S3C_VA_WATCHDOG      S3C_ADDR(0x00400000)     /* watchdog */

#define S3C_VA_UART  S3C_ADDR(0x01000000)     /* UART */

/* This is used for the CPU specific mappings that may be needed, so that

* they do not need to directly used S3C_ADDR() and thus make it easier to

* modify the space for mapping.

*/

#define S3C_ADDR_CPU(x) S3C_ADDR(0x00500000 + (x))

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);

}

由此我们引出了最重要的映射函数:

由前面的s3c_iodesc结构可以知道,6410的寄存器的映射结构体中.type           = MT_DEVICE

static void __init create_mapping(struct map_desc *md)

{

       unsigned long addr, length, end;

       phys_addr_t phys;

       const struct mem_type *type;

       pgd_t *pgd;

       if (md->virtual != vectors_base() && md->virtual < TASK_SIZE) {// TASK_SIZE 0xC0000000 - 16M,所以这里的md->virtual应该是大于这个值才能做map

              printk(KERN_WARNING "BUG: not creating mapping for 0x%08llx"

                     " at 0x%08lx in user region\n",

                     (long long)__pfn_to_phys((u64)md->pfn), md->virtual);

              return;

       }

       if ((md->type == MT_DEVICE || md->type == MT_ROM) &&

           md->virtual >= PAGE_OFFSET && md->virtual < VMALLOC_END) {

              printk(KERN_WARNING "BUG: mapping for 0x%08llx"

                     " at 0x%08lx overlaps vmalloc space\n",

                     (long long)__pfn_to_phys((u64)md->pfn), md->virtual);

       }

//这里有规定就是从0xc0000000 –VMALLOC_END这段空间不能作为MT_DEVICE或者MT_ROM类型的设备的虚拟地址空间的。6410这边的VMALLOC_END0xF6000000,正好是上面我们手动定义的寄存器的虚拟地址空间的基地址。

       type = &mem_types[md->type];

这个的有讲究哦,看下它的代码,当外设的类型为:MT_DEVICE的时候,下面的结构体定义的其实是一级段映射的描述符,结合前面的mmc基础知识,在联系这里的code,是不是有举一反三的效果?

static struct mem_type mem_types[] = {

       [MT_DEVICE] = {                /* Strongly ordered / ARMv6 shared device */

              .prot_pte = PROT_PTE_DEVICE | L_PTE_MT_DEV_SHARED |

                              L_PTE_SHARED,

              .prot_l1   = PMD_TYPE_TABLE,

              .prot_sect       = PROT_SECT_DEVICE | PMD_SECT_S,

              .domain          = DOMAIN_IO,

       },


       /*

        * Catch 36-bit addresses

        */

       if (md->pfn >= 0x100000) {//作为一个4k为基本页面的linux,如果页帧超过0x100000,那么地址空间将超过100000 << 12bit == 4G空间,所以只能作为36bit地址空间来mapping,目前arm中还未有此类型设备。

              create_36bit_mapping(md, type);

              return;

       }

       addr = md->virtual & PAGE_MASK;//addr为页对齐。

       phys = __pfn_to_phys(md->pfn);//页帧转化成页的函数为将页帧左移12bit

       length = PAGE_ALIGN(md->length + (md->virtual & ~PAGE_MASK));

       if (type->prot_l1 == 0 && ((addr | phys | length) & ~SECTION_MASK)) {

              printk(KERN_WARNING "BUG: map for 0x%08llx at 0x%08lx can not "

                     "be mapped using pages, ignoring.\n",

                     (long long)__pfn_to_phys(md->pfn), addr);

              return;

       }

       pgd = pgd_offset_k(addr);

       end = addr + length;

       do {

              unsigned long next = pgd_addr_end(addr, end);

              alloc_init_pud(pgd, addr, next, phys, type);

              phys += next - addr;

              addr = next;

       } while (pgd++, addr != end);

}

这部分代码就是把每个外设的虚拟地址做映射。其实这里面用的还是arm的一级页表映射方式。

总结:

mao_io执行完之后,我们就可以使用虚拟地址来访问寄存器的物理地址空间了,在移植内核的时候一定要注意这个映射的时机,不能在此过程还未执行之前就使用寄存器的虚拟地址,这就会引起莫名其妙的错误哦,

分享到: QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友

返回列表
网页右侧QQ悬浮滚动在线客服
网页右侧QQ悬浮滚动在线客服