分类: 嵌入式
2014-03-27 21:33:36
Linux内核启动过程中注册platform驱动模型
static int __init kernel_init(void * unused) { lock_kernel(); /* * init can allocate pages on any node */ set_mems_allowed(node_possible_map); /* * init can run on any cpu. */ set_cpus_allowed_ptr(current, cpu_all_mask); /* * Tell the world that we're going to be the grim * reaper of innocent orphaned children. * * We don't want people to have to make incorrect * assumptions about where in the task array this * can be found. */ init_pid_ns.child_reaper = current; cad_pid = task_pid(current); smp_prepare_cpus(setup_max_cpus); do_pre_smp_initcalls(); start_boot_trace(); smp_init(); sched_init_smp(); do_basic_setup(); /* * check if there is an early userspace init. If yes, let it do all * the work */ if (!ramdisk_execute_command) ramdisk_execute_command = "/init"; if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) { ramdisk_execute_command = NULL; prepare_namespace(); } /* * Ok, we have completed the initial bootup, and * we're essentially up and running. Get rid of the * initmem segments and start the user-mode stuff.. */ init_post(); return 0; }
/* * Ok, the machine is now initialized. None of the devices * have been touched yet, but the CPU subsystem is up and * running, and memory and process management works. * * Now we can finally start doing some real work.. */ static void __init do_basic_setup(void) { init_workqueues(); cpuset_init_smp(); usermodehelper_init(); init_tmpfs(); driver_init(); init_irq_proc(); do_ctors(); do_initcalls(); }
这个函数向内核注册了一种总线。他首先由/drivers/base/init.c中的driver_init函数调用,driver_init函数由/init/main.c中的do_basic_setup函数调用,do_basic_setup这个函数由kernel_init调用,所以platform总线是在内核初始化的时候就注册进了内核。int __init platform_bus_init(void) { int error; early_platform_cleanup(); error = device_register(&platform_bus); //总线也是设备,所以也要进行设备的注册 if (error) return error; error = bus_register(&platform_bus_type); //注册platform_bus_type总线到内核 if (error) device_unregister(&platform_bus); return error; }
/** * driver_init - initialize driver model. * * Call the driver model init functions to initialize their * subsystems. Called early from init/main.c. */ void __init driver_init(void) { /* These are the core pieces */ devtmpfs_init(); devices_init(); buses_init(); classes_init(); firmware_init(); hypervisor_init(); /* These are also core pieces, but must come after the * core core pieces. */ platform_bus_init(); system_bus_init(); cpu_dev_init(); memory_dev_init(); }
platform总线也是一种设备,这里初始化一个device结构,设备名称platform,因为没有指定父设备,所以注册后将会在/sys/device/下出现platform目录。struct device platform_bus = { .init_name = "platform", };
platform_dev_attrs 设备属性struct bus_type platform_bus_type = { .name = "platform", .dev_attrs = platform_dev_attrs, .match = platform_match, .uevent = platform_uevent, .pm = &platform_dev_pm_ops, };
devs.c中的对应的头文件定义在 kernel/arch/arm/plat-s3c/include/plat/devs.h/* RTC */ static struct resource s3c_rtc_resource[] = { [0] = { //IO端口资源范围 .start = S3C24XX_PA_RTC, .end = S3C24XX_PA_RTC + 0xff, .flags = IORESOURCE_MEM, }, [1] = { //RTC报警中断资源 .start = IRQ_RTC, .end = IRQ_RTC, .flags = IORESOURCE_IRQ, }, [2] = { //TICK节拍时间中断资源 .start = IRQ_TICK, .end = IRQ_TICK, .flags = IORESOURCE_IRQ } }; struct platform_device s3c_device_rtc = { //定义RTC平台设备 .name = "s3c2410-rtc", //设备名称 .id = -1, .num_resources = ARRAY_SIZE(s3c_rtc_resource), //资源数量 .resource = s3c_rtc_resource, //引用上面定义的资源 }; EXPORT_SYMBOL(s3c_device_rtc); //导出该内核函数的符号,即入口地址
mini2440平台设备的系统入口文件为mach-mini2440.cextern struct platform_device s3c_device_fb; extern struct platform_device s3c_device_usb; extern struct platform_device s3c_device_lcd; extern struct platform_device s3c_device_wdt; extern struct platform_device s3c_device_i2c0; extern struct platform_device s3c_device_i2c1; extern struct platform_device s3c_device_rtc; extern struct platform_device s3c_device_adc; extern struct platform_device s3c_device_sdi; extern struct platform_device s3c_device_iis;
平台设备驱动程序:一般在kernel/drivers/下,这里以RTC为例,可以看到kernel/drivers/rtc/rtc-s3c.c:/* devices we initialise */ static struct platform_device *mini2440_devices[] __initdata = { //平台设备数组,定义的平台设备要加入此处 &s3c_device_usb, &s3c_device_rtc,//RTC平台设备 &s3c_device_lcd, &s3c_device_wdt, &s3c_device_i2c0, &s3c_device_iis, &mini2440_device_eth, &s3c24xx_uda134x, &s3c_device_nand, &s3c_device_sdi, &s3c_device_usbgadget, }; static void __init mini2440_machine_init(void) { #if defined (LCD_WIDTH) s3c24xx_fb_set_platdata(&mini2440_fb_info); #endif s3c_i2c0_set_platdata(NULL); s3c2410_gpio_cfgpin(S3C2410_GPC(0), S3C2410_GPC0_LEND); s3c_device_nand.dev.platform_data = &friendly_arm_nand_info; s3c_device_sdi.dev.platform_data = &mini2440_mmc_cfg; platform_add_devices(mini2440_devices, ARRAY_SIZE(mini2440_devices)); s3c_pm_init(); }
static struct platform_driver s3c2410_rtc_driver = { .probe = s3c_rtc_probe, //rtc探测函数 .remove = __devexit_p(s3c_rtc_remove), //rtc移除函数 .suspend = s3c_rtc_suspend, //rtc挂起函数 .resume = s3c_rtc_resume, //rtc恢复函数 .driver = { .name = "s3c2410-rtc",//这里的名称一定要和系统中定义平台设备的地方一致,这样平台设备和平台驱动才能关联起来 .owner = THIS_MODULE, }, }; static char __initdata banner[] = "S3C24XX RTC, (c) 2004,2006 Simtec Electronics\n"; static int __init s3c_rtc_init(void) //驱动模块初始化 { printk(banner); return platform_driver_register(&s3c2410_rtc_driver);//注册rtc平台驱动 } static void __exit s3c_rtc_exit(void) //驱动模块注销 { platform_driver_unregister(&s3c2410_rtc_driver);//注销rtc平台驱动 } module_init(s3c_rtc_init); module_exit(s3c_rtc_exit);