在将Linux移植到目标板的过程中,通常会建立外设I/O内存物理地址到虚拟地址的静态映射:
//用到的结构体
struct map_desc {
unsigned long virtual; //虚拟地址
unsigned long pfn; //__phys_to_pfn(物理地址) , 就是物理页框号
unsigned long length; //大小
unsigned int type; //类型
};
这里是plat级别的map_io:
在linux/arch/arm/plat-s5p/cpu.c中:
/* table of supported CPUs 支持的cpu类型 */
static const char name_s5pv210[] = "S5PV210/S5PC110";
static struct cpu_table cpu_ids[] __initdata = {
{
.idcode = 0x43110000,
.idmask = 0xfffff000,
.map_io = s5pv210_map_io, //这个就是mach级别的map_io
.init_clocks = s5pv210_init_clocks,
.init_uarts = s5pv210_init_uarts,
.init = s5pv210_init,
.name = name_s5pv210,
},
};
/* minimal IO mapping */
static struct map_desc s5p_iodesc[] __initdata = {
{
.virtual = (unsigned long)S5P_VA_CHIPID,
.pfn = __phys_to_pfn(S5P_PA_CHIPID),
.length = SZ_4K, //ARM中页的大小是4K,所以这里的长度必须是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_UART,
.pfn = __phys_to_pfn(S3C_PA_UART),
.length = SZ_4K,
.type = MT_DEVICE,
}, {
.virtual = (unsigned long)VA_VIC0,
.pfn = __phys_to_pfn(S5P_PA_VIC0),
.length = SZ_16K,
.type = MT_DEVICE,
}, {
.virtual = (unsigned long)VA_VIC1,
.pfn = __phys_to_pfn(S5P_PA_VIC1),
.length = SZ_16K,
.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)S5P_VA_GPIO,
.pfn = __phys_to_pfn(S5P_PA_GPIO),
.length = SZ_4K,
.type = MT_DEVICE,
}, {
.virtual = (unsigned long)S3C_VA_WATCHDOG,
.pfn = __phys_to_pfn(S5P_PA_WDT),
.length = SZ_4K,
.type = MT_DEVICE,
}, {
.virtual = (unsigned long)S3C_VA_OTG,
.pfn = __phys_to_pfn(S5P_PA_OTG),
.length = SZ_1M,
.type = MT_DEVICE,
}, {
.virtual = (unsigned long)S3C_VA_OTGPHY,
.pfn = __phys_to_pfn(S5P_PA_OTGPHY),
.length = SZ_1M,
.type = MT_DEVICE,
},
};
void __init s5p_init_io(struct map_desc *mach_desc, int size, void __iomem *cpuid_addr)
{
/* initialize the io descriptors we need for initialization */
iotable_init(s5p_iodesc, ARRAY_SIZE(s5p_iodesc)); //最终建立页映射的函数(这里主要是进行了plat级别的map_io)
idcode = __raw_readl(cpuid_addr);
s3c_init_cpu(idcode, cpu_ids, ARRAY_SIZE(cpu_ids)); //通过这个函数最终会调用mach级别的map_io
}
/kernel/arch/arm/mach-s5pv210/mach-smdkv210.c中:
//板初始化中调用
static void __init smdkv210_map_io(void)
{
s5p_init_io(NULL, 0, S5P_VA_CHIPID);
.....
}
MACHINE_START(SMDKV210, "smdkv210")
.phys_io = S3C_PA_UART & 0xfff00000,
.io_pg_offst = (((u32)S3C_VA_UART) >> 18) & 0xfffc,
.boot_params = S5P_PA_SDRAM + 0x100,
.fixup = s5pv210_fixup,
.init_irq = s5pv210_init_irq,
.map_io = smdkv210_map_io,
.init_machine = smdkv210_machine_init,
.timer = &s5p_systimer,
MACHINE_END
此后,设备驱动中访问经过静态映射的I/O内存时,直接在对应的虚拟地址加上偏移即可,不用需要使用ioremap()。但是我试过用ioremap()也是可以的,不知道有错吗???
补充附上(mach级别的map_io):
在linux/arch/arm/mach-s5pv210/cpu.c中:
/* Initial IO mappings */
static struct map_desc s5pv210_iodesc[] __initdata = {
{
.virtual = (unsigned long)S5P_VA_SYSTIMER,
.pfn = __phys_to_pfn(S5PV210_PA_SYSTIMER),
.length = SZ_1M,
.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)S5P_VA_SROMC,
.pfn = __phys_to_pfn(S5PV210_PA_SROMC),
.length = SZ_4K,
.type = MT_DEVICE,
}, {
.virtual = (unsigned long)S5P_VA_AUDSS,
.pfn = __phys_to_pfn(S5PV210_PA_AUDSS),
.length = SZ_1M,
.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,
}
};
/* s5pv210_map_io
*
* register the standard cpu IO areas
*/
void __init s5pv210_map_io(void)
{
iotable_init(s5pv210_iodesc, ARRAY_SIZE(s5pv210_iodesc));
}