S3c2410nandflash驱动分析
linux2.6.27
1.平台设备的注册
1.1平台设备的结构体:(.../arch/arm/plat-s3c24xx/devs.c)
struct platform_device s3c_device_nand = {
.name = "s3c2410-nand",
.id = -1,
.num_resources = ARRAY_SIZE(s3c_nand_resource),
.resource = s3c_nand_resource,
};
static struct resource s3c_nand_resource[] = {
[0] = {
.start = S3C2410_PA_NAND,
.end = S3C2410_PA_NAND + S3C24XX_SZ_NAND - 1,
.flags = IORESOURCE_MEM,
}
};
1.2将该结构体添加到所有平台结构的数组中,以mach-qt2410这种机器配置为例:
.../arch/arm/mach-s3c2410/mach-qt2410.c
static struct platform_device *qt2410_devices[] __initdata = {
....
&s3c_device_nand,
};
1.3 分区信息
static struct mtd_partition qt2410_nand_part[] = {
[0] = {
.name = "U-Boot",
.size = 0x30000,
.offset = 0,
},
[1] = {
.name = "U-Boot environment",
.offset = 0x30000,
.size = 0x4000,
},
[2] = {
.name = "kernel",
.offset = 0x34000,
.size = SZ_2M,
},
[3] = {
.name = "initrd",
.offset = 0x234000,
.size = SZ_4M,
},
[4] = {
.name = "jffs2",
.offset = 0x634000,
.size = 0x39cc000,
},
};
static struct s3c2410_nand_set qt2410_nand_sets[] = {
[0] = {
.name = "NAND",
.nr_chips = 1,
.nr_partitions = ARRAY_SIZE(qt2410_nand_part),
.partitions = qt2410_nand_part,
},
};
static struct s3c2410_platform_nand qt2410_nand_info = {
.tacls = 20,
.twrph0 = 60,
.twrph1 = 20,
.nr_sets = ARRAY_SIZE(qt2410_nand_sets),
.sets = qt2410_nand_sets,
};
1.4 注册nand平台设备
static void __init qt2410_machine_init(void)
{
// 将分区信息设为平台的平台数据.这样能过平台设备就可以得到分区信息.
s3c_device_nand.dev.platform_data = &qt2410_nand_info;
...
// 注册平台设备.
platform_add_devices(qt2410_devices, ARRAY_SIZE(qt2410_devices));
...
}
这个函数是如何在系统启动时被调用的呢:
内核启动时将运行head.S,再其中将调用.../init/main.c中的start_kernel的函数
asmlinkage void __init start_kernel(void) { ...... setup_arch(&command_line); // 1.找到所属的machine_desc,即在arch/arm/mach-s3c2410/qt2410.c中最后定义的一个结构。 // 2.paging_init(&meminfo, mdesc)->devicemaps_init(mdesc)->mdesc->map_io(); // 3.找到该machine_desc定义的初始化函数句柄: init_arch_irq = mdesc->init_irq; system_timer = mdesc->timer; init_machine = mdesc->init_machine; ...... init_IRQ(); // 1.初始化了irq_desc数组 // 2.调用了init_arch_irq(),即调用mdesc->init_irq。 ...... time_init(); // 1.system_timer->init(); } arch_initcall(customize_machine); customize_machine(void)->init_machine() 即调用 mdesc->init_machine( qt2410_machine_init(void))
在start_kernel中将根据bootloader中传递过来MACHINE Id查询到对应的machine_desc,
在mach-qt2410.c的最后我们定义了下machine_desc结构:
MACHINE_START(QT2410, "QT2410")
.phys_io = S3C2410_PA_UART,
.io_pg_offst = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
.boot_params = S3C2410_SDRAM_PA + 0x100,
.map_io = qt2410_map_io,
.init_irq = s3c24xx_init_irq,
.init_machine = qt2410_machine_init,
.timer = &s3c24xx_timer,
MACHINE_END
2.平台驱动的注册
.../drivers/mtd/nand/s3c2410.c
2.1平台驱动
static struct platform_driver s3c2410_nand_driver = {
.probe = s3c2410_nand_probe,
.remove = s3c2410_nand_remove,
.suspend = s3c24xx_nand_suspend,
.resume = s3c24xx_nand_resume,
.driver = {
.name = "s3c2410-nand",
.owner = THIS_MODULE,
},
};
2.2 驱动的加载
static int __init s3c2410_nand_init(void)
{
...
return platform_driver_register(&s3c2410_nand_driver);
}
module_init(s3c2410_nand_init);
在platform_driver_register的处理过程中,将会在platform总线下注册一个驱动,并且在platform总线下挂有的所有设备中查找名称为"s3c2410-nand"的设备,并调用驱动的probe方法.
2.3 probe方法
static int s3c2410_nand_probe(struct platform_device *dev)
{
return s3c24xx_nand_probe(dev, TYPE_S3C2410);
}
在 s3c24xx_nand_probe中主要作了以下几个工作:
创建添充struct s3c2410_nand_info
创建添充struct s3c2410_nand_mtd[]数组
创建添充struct mtd_info
创建添充struct nand_chip
扫描芯片形成芯片的BBT
最后调用 add_mtd_partitions注册所有分区.
最烦锁的是前5步.
struct s3c2410_nand_info主要包括一个nand_hw_control,一个struct s3c2410_nand_mtd[]数组,和平台设备指针,物理内存,时钟,配置寄存器,平台数据.等.
每一个struct s3c2410_nand_mtd包括一个struct mtd_info结构和一个struct nand_chip结构.
四种结构的相互引用关系为:
s3c2410_nand_mtd->info = s3c2410_nand_info
mtd_info->priv = nand_chip
nand_chip->priv = s3c2410_nand_mtd