通常在Linux中,把SOC系统中集成的独立外设单元(如:I2C、IIS、RTC、看门狗等)都被当作平台设备来处理。在Linux中用platform_device结构体来描述一个平台设备,在2.6.32内核中定义在:include/linux/platform_device.h中,如下:
在Linux中是用这个结构体来定义一些平台设备的。在arch/arm/plat-s3c24xx/devs.c中就定义了很多平台设备。
平台设备驱动是指具体的某种平台设备的驱动,比如RTC平台设备驱动。在Linux中,系统还为平台设备定义了平台驱动结构体platform_driver,就好比系统为字符设备定义了file_operations一样,但不要把平台设备跟字符设备、块设备、网络设备搞成了并列的概念,因平台设备也可以是字符设备等其他设备。注意:在被定义为平台设备的字符设备的驱动中,除了要实现字符设备驱动中file_operations的open、release、read、write等接口函数外,还要实现平台设备驱动中platform_driver的probe、remove、suspend、resume等接口函数。
以S3c2440开发板为例,在linux-2.6.30.4/arch/arm/plat-s3c24xx/common-smdk.c中定义了如下设备:
staticstruct platform_device __initdata *smdk_devs[] = { //
平台设备列表
1
,也就是说我们要使用一个新的平台设备要先在上面定义,然后加到这个列表中,最后到驱动层去实现该设备的驱动
&s3c_device_nand,
&s3c_device_sdi,
&smdk_led4,
&smdk_led5,
&smdk_led6,
&smdk_led7,
};
在linux-2.6.30.4/arch/arm/mach-s3c2440/mach-smdk2440.c中定义了如下设备:
static struct platform_device*smdk2440_devices[] __initdata = { //
平台设备列表
2
&s3c_device_usb,
&s3c_device_lcd,
&s3c_device_wdt,
&s3c_device_i2c0,
&s3c_device_iis,
&s3c_device_rtc,
&s3c_device_dm9000,
&s3c_device_uda134x,
};
这些设备在smdk2440_machine_init函数中,通过platform_add_device函数注册进内核。
linux-2.6.30.4/arch/arm/mach-s3c2440/mach-smdk2440.c
static void __initsmdk2440_machine_init(void)
{
s3c24xx_fb_set_platdata(&smdk2440_fb_info);
s3c_i2c0_set_platdata(NULL);
platform_add_devices(smdk2440_devices,ARRAY_SIZE(smdk2440_devices));
//
将上面列表中的平台设备添加到系统总线中
smdk_machine_init();
}
linux-2.6.30.4/arch/arm/plat-s3c24xx/common-smdk.c
void __init smdk_machine_init(void)
{
……
s3c_device_nand.dev.platform_data =&smdk_nand_info;
platform_add_devices(smdk_devs,ARRAY_SIZE(smdk_devs));
//
将上面列表中的平台设备添加到系统总线中
s3c_pm_init();
}
以实时时钟RTC 驱动为例
linux/arch/arm/plat-s3c24xx/devs.c中:
struct platform_device s3c_device_rtc = {
.name = "s3c2410-rtc",
.id = -1,
.num_resources =ARRAY_SIZE(s3c_rtc_resource),
.resource = s3c_rtc_resource,
};
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,
},
};
在linux/arch/arm/plat-s3c24xx/devs.c中
static struct resource s3c_rtc_resource[] = {//定义了RTC平台设备使用的资源,这些资源在驱动中都会用到
[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
}
};
static int __init s3c_rtc_init(void)
{
/*将RTC注册成平台设备驱动*/
return platform_driver_register(&s3c2410_rtc_driver);
}
static void __exit s3c_rtc_exit(void)
{
/*注销RTC平台设备驱动*/
platform_driver_unregister(&s3c2410_rtc_driver);
}
module_init(s3c_rtc_init);
module_exit(s3c_rtc_exit);
RTC设备类的操作,是对RTC硬件的各种寄存器进行操作,rtc_class_ops是RTC设备类在RTC驱动核心部分中定义的对RTC设备类进行操作的结构体,类似字符设备在驱动中的file_operations对字符设备进行操作的意思。对RTC的操作主要有打开、关闭、设置或获取时间、设置或获取报警、设置节拍时间计数值等等,该结构体内接口函数的实现都在下面
static const struct rtc_class_ops s3c_rtcops = {
.open = s3c_rtc_open,
.release = s3c_rtc_release,
.irq_set_freq = s3c_rtc_setfreq, /*在第②步中已实现*/
.irq_set_state = s3c_rtc_setpie,
.read_time = s3c_rtc_gettime,
.set_time = s3c_rtc_settime,
.read_alarm = s3c_rtc_getalarm,
.set_alarm = s3c_rtc_setalarm,
};
机理:内核启动后,首先够造链表将描述设备的platform_device结构组织起来,得到一个设备的列表;当加载到某个驱动程序的platform_driver结构时(start_kernelàrest_init()àinit àdo_basic_setup(); //初始化设备驱动程序),使用一些匹配函数来检查驱动程序是否支持这些设备,常用的检查方法很简单:比较驱动程序和设备的名称。
当注册成功时会调用platform_driver结构元素probe函数指针,这里就是s3c_rtc_probe。驱动的初始化等都是在prob函数中完成的。