Linux驱动开发17之系统启动时platform_device在哪里注册?

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值