在將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));
}