platform_device 平台设备结构体加载到系统中*/
platform_add_devices(smdk_devs, ARRAY_SIZE(smdk_devs));
s3c_pm_init();
}
其实smdk_machine_init函数在是在/arch/arm/mach-s3c2440/mach-smdk2440.c中被smdk_machine_init函数调用的而后者又通过machine_desc结构体(通过MACHINE_START与MACHINE_END来定义的),在内核启动时通过setup_arch被加载到系统中的。
那么smdk_nand_info中的设备相关的信息又是怎么添加的呢,同样在在arch/arm/plat-s3c24xx/common-smdk.c中:
static struct s3c2410_platform_nand smdk_nand_info = {
.tacls = 20, /* time for active CLE/ALE to nWE/nOE */
.twrph0 = 60, /* active time for nWE/nOE */
.twrph1 = 20, /* time for release CLE/ALE from nWE/nOE inactive */
.nr_sets = ARRAY_SIZE(smdk_nand_sets),
.sets = smdk_nand_sets,
};
从上看出,tacls,twrph0,twrph1是nand flash时序相关的设置,他们分别对应nand flash芯片手册上的tCLS (CLE setup Time),tWP (WE Pulse Width),tCLH (CLE Hold Time)。这些参数需要根据具体的nand flash 芯片进行相应设置。
其中.sets成员的赋值如下:
static struct s3c2410_nand_set smdk_nand_sets[] = {
[0] = {
.name = "NAND",
.nr_chips = 1,
.nr_partitions = ARRAY_SIZE(smdk_default_nand_part),
.partitions = smdk_default_nand_part,
},
};
sets中的一个元素就对应一片nand flash芯片的分区信息,主要是.partitions成员,其赋值如下:
static struct mtd_partition smdk_default_nand_part[] = {
[0] = {
.name = "Boot Agent",
.size = SZ_16K,
.offset = 0,
},
//此处略去一些分区定义,完整的查看相应代码。
[7] = {
.name = "S3C2410 flash partition 7",
.offset = SZ_1M * 48,
.size = SZ_16M,
}
};
上面这个数组就是自定义的nand flash的分区情况,我们可以根据自己的分区要求进行相应的修改。
至此,对平台设备结构体platform_device的分析就差不多了(包括赋值与加载过程)。但是驱动程序结构体platform_driver是怎么定义与加载的呢?答案在drivers/mtd/nand/s3c2410.c中:
static struct platform_driver s3c24xx_nand_driver = {
.probe = s3c24xx_nand_probe,
.remove = s3c24xx_nand_remove,
.suspend = s3c24xx_nand_suspend,
.resume = s3c24xx_nand_resume,
.id_table = s3c24xx_driver_ids,
.driver = {
.name = "s3c24xx-nand",
.owner = THIS_MODULE,
},
};
这个结构体中的.probe成员很重要,在我们向系统中添加了一个设备后,并匹配到相应的驱动结构体后,系统会自动调用该函数,来申请platform_device中定义的设备资源。并且初始话相应的硬件操作函数。
可以参考 s3c24xx_nand_probe的代码。内核自带的代码已经提供了丰富的s3c24**系列开发板的支持代码了, s3c24xx_nand_probe调用s3c2410_nand_inithw函数完成相应的nand flash操作函数的对号入座。这还依靠到了cpu_type这个int来实现的,这个值是根据platform_device中的.name成员来确定的。
另外,.id_table这个成员,用来实现注册一个platform_dirver对应多个platform_device的。就如s3c24**的nand 驱动都用的是同一个platform_dirver 结构体:s3c24xx_nand_driver。设备之间的区别是通过platform_device_id的.driver_data成员区分的,这个值就是前面提到的cpu_type的值来源,从而在连接设备与驱动时可以针对设备区别对加载的驱动程序进行调整。
细心观察你会发现,2440用的platform_device结构体也是
struct platform_device s3c_device_nand = {
.name = "s3c2410-nand",
.id = -1,
.num_resources = ARRAY_SIZE(s3c_nand_resource),
.resource = s3c_nand_resource,
};
那么加载驱动程序不是也加载2410的了吗?
答案在arch/arm/plat-s3c24xx/s3c244x.c中:
void __init s3c244x_map_io(void)
{
/* register our io-tables */
iotable_init(s3c244x_iodesc, ARRAY_SIZE(s3c244x_iodesc));
/* rename any peripherals used differing from the s3c2410 */
s3c_device_sdi.name = "s3c2440-sdi";
s3c_device_i2c0.name = "s3c2440-i2c";
s3c_device_nand.name = "s3c2440-nand";
s3c_device_usbgadget.name = "s3c2440-usbgadget";
}
具体调用关系如下:
/arch/arm/mach-s3c2440/mach-smdk2440.c中的:
smdk2440_map_io---》 s3c24xx_init_io---》s3c_init_cpu---》cpu->map_io--》s3c244x_map_io---》s3c_device_nand.name = "s3c2440-nand";
从而跟新了platform_nand的.name选项。这样2440的驱动就能很好的加载了。
下面的函数实现驱动加载:
static int __init s3c2410_nand_init(void)
{
printk("S3C24XX NAND Driver, (c) 2004 Simtec Electronics\n");
return platform_driver_register(&s3c24xx_nand_driver);
}
总结:2440的nand flash的移植我们只需要关注nand时序,以及自己需要的分区情况就行了。并在相应的目录位子添加进我们的板子相关文件mach-s3c2440.....下的.c文件