1.系统启动的时候,platform_device的注册在哪里?
答案:先去找启动过程中的C语言阶段,下面就是描述,我们一般看红色字体的部分。总结下来就是找:
1)do_initcalls,这个比较分散,
2)init_machine,这个比较集中,但实际上是被do_initcalls调用的!
下面来具体分析一下。
2.init_machine和do_initcalls的调用关系
当然在setup_arch()中对init_machine进行初始化,这个时候并没有调用init_machine函数,init_machine是在代码中被定义为arch_initcall属性(arch/arm/kernel/setup.c), 然后在do_initcalls()中进行遍历调用,具体见启动过程:
Start_kernel() èsetup_arch() èreset_init() è kernel_thread(init …) è init() è
do_basic_setup() èdriver_init() è do_initcall()
00779: static void __init do_initcalls(void)
00780: {
00781: initcall_t *fn;
00783: for (fn = __early_initcall_end; fn <
00783: __initcall_end; fn++)
00784: do_one_initcall(*fn);
00786: /*
00786: Make sure there is no pending stuff from the initcall sequence
00787: flush_scheduled_work();
00788: }
分析上面一段代码可以看出,设备的初始化是通过do_basic_setup()函数调用do_initcalls()函数,实现__initcall_start, __initcall_end段之间的指针函数执行的。而到底是那些驱动函数怎么会被集中到这个段内的呢?我们知道系统内存空间的分配是由链接器ld读取链接脚本文件决定。链接器将同样属性的文件组织到相同的段里面去,如所有的.text段都被放在一起。在链接脚本里面可以获得某块内存空间的具体地址。我们来看下linux-2.6.18.8/arch/arm/kernel/vmlinux.lds.S文件。由于文件过长,只贴出和__initcall_start, __initcall_end相关的部分。
__initcall_start = .;
*(.initcall1.init)
*(.initcall2.init)
*(.initcall3.init)
*(.initcall4.init)
*(.initcall5.init)
*(.initcall6.init)
*(.initcall7.init)
__initcall_end = .;
从脚本文件中我们可以看出, 在__initcall_start, __initcall_end之间放置的是属行为(.initcall*.init)的函数数据 。在linux/include/linux/init.h文件中可以知道,(.initcall*.init)属性是由__define_initcall(level, fn)宏设定的。
#define __define_initcall(level,fn) /
static initcall_t __initcall_##fn __attribute_used__ /
__attribute__((__section__(".initcall" level ".init"))) = fn
#define core_initcall(fn) __define_initcall("1",fn)
#define postcore_initcall(fn) __define_initcall("2",fn)
#define arch_initcall(fn) __define_initcall("3",fn)
#define subsys_initcall(fn) __define_initcall("4",fn)
#define fs_initcall(fn) __define_initcall("5",fn)
#define device_initcall(fn) __define_initcall("6",fn)
#define late_initcall(fn) __define_initcall("7",fn)
#define __initcall(fn) device_initcall(fn)
由此可以判断,所有的设备驱动函数都必然通过*_initcall(fn)宏的处理。以此为入口,可以查询所有的设备驱动。
3.MACHINE_START与MACHINE_END
在arch/arm/mach-s5pv210/mach-x210.c中。
/*调用MACHINE_START宏
MACHINE_START和MACHINE_END框起了一个machine_desc结构体的声明并根据MACHINE_START宏的参数初始化其.nr和.name成员,并将该结构体标记编译到.arch.info.init段,
在MACHINE_START和MACHINE_END宏之间可以初始化machine_desc结构体的剩余成员
*/
#ifdef CONFIG_MACH_SMDKC110
MACHINE_START(SMDKC110, "SMDKC110")
#elif CONFIG_MACH_SMDKV210
MACHINE_START(SMDKV210, "SMDKV210")
#endif
/* Maintainer: Kukjin Kim <kgene.kim@samsung.com> */
.phys_io = S3C_PA_UART & 0xfff00000,
.io_pg_offst = (((u32)S3C_VA_UART) >> ) & 0xfffc,
.boot_params = S5P_PA_SDRAM + 0x100,
.init_irq = s5pv210_init_irq,//板级中断初始化函数
.map_io = smdkc110_map_io,//板级io初始化函数
.init_machine = smdkc110_machine_init,//板级初始化函数
.timer = &s5p_systimer,
MACHINE_END
5.smdkc110_machine_init
platform_add_devices(smdkc110_devices, ARRAY_SIZE(smdkc110_devices));
static struct platform_device *smdkc110_devices[] __initdata = {。。。。。}
6.smdkc110_map_io
板级io的初始化也包括部分外设的初始化。我们后面跟踪下串口设备的初始化。
static void __init smdkc110_map_io(void)
s3c24xx_init_uarts(smdkc110_uartcfgs, ARRAY_SIZE(smdkc110_uartcfgs));
static struct s3c2410_uartcfg smdkc110_uartcfgs[] __initdata = {。。}
下面都是串口的调用注册过程!
7.cpu_ids
/* table of supported CPUs */
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,
},
};
arch/arm/plat-samsung/init.c
void __init s3c24xx_init_uarts(struct s3c2410_uartcfg *cfg, int no)
{
if (cpu == NULL)
return;
if (cpu->init_uarts == NULL) {
printk(KERN_ERR "s3c24xx_init_uarts: cpu has no uart init\n");
} else
(cpu->init_uarts)(cfg, no);//这里最终会调用上面的s5pv210_init_uarts
}
8.s5pv210_common_init_uarts
#define s5pv210_init_uarts s5pv210_common_init_uarts
/* uart registration process */
void __init s5pv210_common_init_uarts(struct s3c2410_uartcfg *cfg, int no)
{
struct s3c2410_uartcfg *tcfg = cfg;
u32 ucnt;
; ucnt < no; ucnt++, tcfg++) {
if (!tcfg->clocks) {
tcfg->clocks = s5pv210_serial_clocks;
tcfg->clocks_size = ARRAY_SIZE(s5pv210_serial_clocks);
}
}
s3c24xx_init_uartdevs("s5pv210-uart", s5p_uart_resources, cfg, no);
}
9. s3c24xx_init_uartdevs
arch/arm/plat-samsung/init.c
void __init s3c24xx_init_uartdevs(char *name,
struct s3c24xx_uart_resources *res,
struct s3c2410_uartcfg *cfg, int no)
{
struct platform_device *platdev;
struct s3c2410_uartcfg *cfgptr = uart_cfgs;
struct s3c24xx_uart_resources *resp;
int uart;
memcpy(cfgptr, cfg, sizeof(struct s3c2410_uartcfg) * no);
; uart < no; uart++, cfg++, cfgptr++) {
platdev = s3c24xx_uart_src[cfgptr->hwport];
resp = res + cfgptr->hwport;
s3c24xx_uart_devs[uart] = platdev;
platdev->name = name;
platdev->resource = resp->resources;
platdev->num_resources = resp->nr_resources;
platdev->dev.platform_data = cfgptr;//将cfg挂到platdev->dev.platform_data上
}
nr_uarts = no;
}
static int __init s3c_arch_init(void)
{
int ret;
// do the correct init for cpu
if (cpu == NULL)
panic("s3c_arch_init: NULL cpu\n");
ret = (cpu->init)();
)
return ret;
ret = platform_add_devices(s3c24xx_uart_devs, nr_uarts);
return ret;
}
arch_initcall(s3c_arch_init);