Chinaunix首页 | 论坛 | 博客
  • 博客访问: 338188
  • 博文数量: 95
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 157
  • 用 户 组: 普通用户
  • 注册时间: 2014-03-26 20:03
文章分类

全部博文(95)

文章存档

2017年(15)

2015年(17)

2014年(63)

分类: 嵌入式

2014-03-27 21:33:36



Linux内核启动过程中注册platform驱动模型      

 

Linux内核启动过程中注册platform驱动模型

分类: 嵌入式 Linux 驱动开发 Platform驱动框架 528人阅读 评论(0) 举报
 一、platform设备和驱动与linux设备模型密切相关。platform在linux设备模型中,其实就是一种虚拟总线没有对应的硬件结构。它的主要作用就是管理系统的外设资源,比如io内存,中断信号线。现在大多数处理器芯片都是soc,如s3c2440,它包括处理器内核(arm920t)和系统的外设(lcd接口,nandflash接口等)。linux在引入了platform机制之后,内核假设所有的这些外设都挂载在platform虚拟总线上,以便进行统一管理。
 二、platform 总线
        1、在系统中platform对应的文件drivers/base/platform.c,它不是作为一个模块注册到内核的,关键的注册总线的函数由系统初始化部分,对应/init/main.c中的do_basic_setup函数间接调用。这里可以看出platform非常重要,要在系统其他驱动加载之前注册。下面分析platform总线注册函数
kernel/init/main.c ------> kernel_init()
		
		

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

kernel/init/main.c ------>do_basic_setup()
		
		

/* * 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(); }

		
		

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

这个函数向内核注册了一种总线。他首先由/drivers/base/init.c中的driver_init函数调用,driver_init函数由/init/main.c中的do_basic_setup函数调用,do_basic_setup这个函数由kernel_init调用,所以platform总线是在内核初始化的时候就注册进了内核。
kernel/drivers/base/init.c------->driver_init()
		
		

/** * 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(); }

    2.、platform_bus_type 总线结构与设备结构
(1) platform总线 设备结构
		
		

struct device platform_bus = { .init_name = "platform", };

platform总线也是一种设备,这里初始化一个device结构,设备名称platform,因为没有指定父设备,所以注册后将会在/sys/device/下出现platform目录。
(2) platform总线 总线结构
		
		

struct bus_type platform_bus_type = { .name = "platform", .dev_attrs = platform_dev_attrs, .match = platform_match, .uevent = platform_uevent, .pm = &platform_dev_pm_ops, };

platform_dev_attrs    设备属性
platform_match          match函数,这个函数在当属于platform的设备或者驱动注册到内核时就会调用,完成设备与驱动的匹配 工作。
 platform_uevent       热插拔操作函数

$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
实例:
platform device结构体和相关结构体、函数定义见: kernel/include/linux/platfrom_device.h
platform device相关的函数实现见: kernel/drivers/base/platform.c
mini2440内核注册platform设备过程:
一种soc确定之后,其外设模块就已经确定了,所以注册platform设备就由板级初始化代码来完成,在mini2440中是mach-mini2440.c的mini2440_machine_init函数中调用platform_add_devices(mini2440_devices, ARRAY_SIZE(mini2440_devices))来完成注册。这个函数完成mini2440的所有platform设备的注册:
    (1) platform_add_devices函数是platform_device_register的简单封装,它向内核注册一组platform设备
    (2) mini2440_devices是一个platform_device指针数组,详见后面的代码

这里以mini2440开发板里的相关平台设备为例:如rtc平台驱动
板载资源的相关平台设备定义 kernel/arch/arm/plat-s3c24xx/devs.c中:
		
		

/* 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); //导出该内核函数的符号,即入口地址

devs.c中的对应的头文件定义在 kernel/arch/arm/plat-s3c/include/plat/devs.h
		
		

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

mini2440平台设备的系统入口文件为mach-mini2440.c
kernel/arch/arm/mach-s3c2440/mach-mini2440.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(); }

平台设备驱动程序:一般在kernel/drivers/下,这里以RTC为例,可以看到kernel/drivers/rtc/rtc-s3c.c:
		
		

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

因此,我们可以参考这里的rtc平台驱动的分析步骤,来进行mach-mini2440.c中的其他的平台设备驱动。http://blog.csdn.net/gqb_driver/article/details/8549132http://blog.csdn.net/gqb_driver/article/details/8549132http://blog.csdn.net/gqb_driver/article/details/8549132
阅读(867) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~