1,platform_driver 的定义和注册
在s3c_nand.c中,
static struct platform_driver s3c6410_nand_driver = {
.probe = s3c6410_nand_probe,
.remove = s3c_nand_remove,
.suspend = s3c_nand_suspend,
.resume = s3c_nand_resume,
.driver = {
.name = "s3c6410-nand",
.owner = THIS_MODULE,
},
};
static int __init s3c_nand_init(void)
{
printk("S3C NAND Driver, (c) 2008 Samsung Electronics/n");
return platform_driver_register(&s3c6410_nand_driver);
}
module_init(s3c_nand_init);
module_exit(s3c_nand_exit);
与大多数嵌入式Linux 驱动一样,NAND 驱动也是从module_init 宏开始。s3c_nand_init 是驱动初始化函数,在此函数中注册platform driver 结构体,platform driver 结构体中自然需要定义probe 和remove 函数。其实在大多数嵌入式Linux 驱动中,这样的套路基本已经成了一个定式。
至于module_init 有什么作用,s3c_nand_init 又是何时调用的,以及这个driver 是怎么和NAND 设备联系起来的,就不再多说了,这里只提三点:
A)以上代码只是向内核注册了NAND 的platform_driver ,即s3c_nand_init ,我们当然还需要一个NAND 的platform_device ,要不然s3c_nand_init的probe 函数就永远不会被执行,因为没有device 需要这个driver 。
B)向Linux 内核注册NAND 的platform_device 有两种方式:其一是直接定义一个NAND 的platform_device 结构体,然后调用platform_device_register 函数注册。其二是用platform_device_alloc 函数动态分配一个platform_device ,然后再用platform_device_add 函数把这个platform_device 加入到内核中去。相对来说,第一种方式更加方便和直观一点,而第二种方式则更加灵活一点。
C)在加载NAND 驱动时,我们还需要向MTD Core 提供一个信息,那就是NAND 的分区信息。
2,platform_device 的定义和注册
在本地代码上,platform_device是这样注册的:
A)定义了分区表:
struct mtd_partition s3c_partition_info[] = {
{
.name = "misc",
.offset = (768*SZ_1K), /* for bootloader */
.size = (256*SZ_1K),
.mask_flags = MTD_CAP_NANDFLASH,
},
{
.name = "recovery",
.offset = MTDPART_OFS_APPEND,
.size = (5*SZ_1M),
// .mask_flags = MTD_CAP_NANDFLASH,
},
{
.name = "kernel",
.offset = MTDPART_OFS_APPEND,
.size = (3*SZ_1M),
},
{
.name = "ramdisk",
.offset = MTDPART_OFS_APPEND,
.size = (1*SZ_1M),
},
{
.name = "system",
.offset = MTDPART_OFS_APPEND,
.size = (67*SZ_1M),
},
{
.name = "cache",
.offset = MTDPART_OFS_APPEND,
.size = (67*SZ_1M),
},
{
.name = "userdata",
.offset = MTDPART_OFS_APPEND,
.size = MTDPART_SIZ_FULL,
}
};
其中offset是分区开始的偏移地址,MTDPART_OFS_APPEND,表示紧接着上一个分区,MTD Core会自动计算和处理分区地址;size是分区的大小,在最后一个分区我们设为MTDPART_SIZ_FULL,表示这个NAND剩下的所有部分。这样配置NAND的分区并不是唯一的,需要视具体的系统而定,我们可以在kernel中这样显式的指定,也可以使用bootloader传给内核的参数进行配置。
B)struct s3c_nand_mtd_info s3c_nand_mtd_part_info = {
.chip_nr = 1,
.mtd_part_nr = ARRAY_SIZE(s3c_partition_info),
.partition = s3c_partition_info,
};
C)填充s3c_device_nand.dev.platform_data = &s3c_nand_mtd_part_info;
D)s3c_device_nand是struct platform_device *smdk6410_devices[]的一个成员,然后platform_add_devices(smdk6410_devices, ARRAY_SIZE(smdk6410_devices));一起加载全部的platform_device(与platform_device_add相比前者的好处是可以一次加载多个device)
3,对分区的加载应用
一个MTD原始设备可以通过mtd_part分割成数个MTD原始设备注册进mtd_table,mtd_table中的每个MTD原始设备都可以被注册成一个MTD设备,有两个函数可以完成这个工作,即 add_mtd_device函数和add_mtd_partitions函数。其中add_mtd_device函数是把整个NAND FLASH注册进MTD Core,而add_mtd_partitions函数则是把NAND FLASH的各个分区分别注册进MTD Core。其中master就是这个MTD原始设备,parts即NAND的分区信息,nbparts指有几个分区。
这个可以从s3c_nand_probe中的add_mtd_partitions(s3c_mtd, partition_info, plat_info->mtd_part_nr);获得,其中的partition_info就是3c_partition_info,第三个参数就是ARRAY_SIZE(s3c_partition_info)。
参考原文:http://blog.csdn.net/leibniz_zsu/archive/2009/12/10/4977650.aspx