最近在做Linux内核移植,总体的感觉是这样的,想要彻底的阅读Linux内核代码几乎是不可能的,至少这还不是嵌入式学期初期的重要任务。内核代码解压后有250M左右,据统计,有400多万行,而且涉及到了软件和硬件两方面的诸多知识,凭一人之力在短时间内阅读Linux内核代码是根本不可能的,强行阅读可能会打消我们嵌入式学习的积极性,最后甚至可能放弃嵌入式学习,如果真的想阅读内核代码来提高自己水平的话可以等熟练掌握嵌入式以后再回过头来阅读,这样理解也会更深刻,更透彻。
我认为Linux内核移植的初期阶段应该将重点放在分析内核设备驱动上。实际上,Linux内核的移植就是设备驱动的移植,内核本身不会直接访问硬件,是通过驱动程序来间接控制硬件的,而其他的高级功能如内存管理,进程管理等是通用的,无需做其他配置,所以我们只需要配置相关的驱动即可实现Linux内核移植。驱动移植的关键在于了解在驱动的结构,本文将以Nand驱动为例,分析Linux内核的驱动结构。
在分析驱动结构之前,还需要了解下内核识别设备的方式,内核通过驱动程序识别设备的方法有两种,一种是驱动程序本身带有设备信息,比如开始地址、中断号等,加载驱动时就可以根据驱动中的信息来识别设备;另一种是驱动程序本身没有设备信息,但是内核中已经根据其他方式确定了很多设备信息,加载驱动时将驱动程序与这些设备逐个比较,确定两者是否匹配,如果匹配就可以使用该驱动来识别设备了。内核常采用的是第二种方式,这样方式可将各种设备集中在一个文件中管理,当开发板的配置改变时便于修改代码。对应的,内核文件include/linux/platform_device.h中定义了两个结构,一个是platform_device,用来描述设备信息,一个是platform_driver,用来描述驱动信息,内核启动后首先构造链表将plartfrom_device结构组织起来得到一个设备链表,当加载某个驱动时根据platform_driver提供的信息与设备链表一一进行匹配,这就是内核设备识别的大体过程,具体的过程比这复杂很多,这里不做过多研究。下面我们开始分析Linux内核的Nand驱动。
这里以Linux内核的3.5.3中默认的mini2440开发板为例,首先定位到arm/arm/mach-s3c24xx/mach-mini2440.c,然后找到如下结构:
- static struct platform_device *mini2440_devices[] __initdata = {
- &s3c_device_ohci,
- &s3c_device_wdt,
- &s3c_device_i2c0,
- &s3c_device_rtc,
- &s3c_device_usbgadget,
- &mini2440_device_eth,
- &mini2440_led1,
- &mini2440_led2,
- &mini2440_led3,
- &mini2440_led4,
- &mini2440_button_device,
- &s3c_device_nand,
- &s3c_device_sdi,
- &s3c_device_iis,
- &uda1340_codec,
- &mini2440_audio,
- &samsung_asoc_dma,
- };
- static struct platform_device *mini2440_devices[] __initdata = {
- &s3c_device_ohci,
- &s3c_device_wdt,
- &s3c_device_i2c0,
- &s3c_device_rtc,
- &s3c_device_usbgadget,
- &mini2440_device_eth,
- &mini2440_led1,
- &mini2440_led2,
- &mini2440_led3,
- &mini2440_led4,
- &mini2440_button_device,
- &s3c_device_nand,
- &s3c_device_sdi,
- &s3c_device_iis,
- &uda1340_codec,
- &mini2440_audio,
- &samsung_asoc_dma,
- };
- platform_add_devices(mini2440_devices, ARRAY_SIZE(mini2440_devices));
- platform_add_devices(mini2440_devices, ARRAY_SIZE(mini2440_devices));
- static struct resource s3c_nand_resource[] = {
- [0] = DEFINE_RES_MEM(S3C_PA_NAND, SZ_1M),
- };
- 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] = DEFINE_RES_MEM(S3C_PA_NAND, SZ_1M),
- };
- struct platform_device s3c_device_nand = {
- .name = "s3c2410-nand",
- .id = -1,
- .num_resources = ARRAY_SIZE(s3c_nand_resource),
- .resource = s3c_nand_resource,
- };
- struct resource {
- resource_size_t start;
- resource_size_t end;
- const char *name;
- unsigned long flags;
- struct resource *parent, *sibling, *child;
- };
- struct resource {
- resource_size_t start;
- resource_size_t end;
- const char *name;
- unsigned long flags;
- struct resource *parent, *sibling, *child;
- };
- #define DEFINE_RES_NAMED(_start, _size, _name, _flags) \
- { \
- .start = (_start), \
- .end = (_start) + (_size) - 1, \
- .name = (_name), \
- .flags = (_flags), \
- }
- #define DEFINE_RES_MEM_NAMED(_start, _size, _name) \
- DEFINE_RES_NAMED((_start), (_size), (_name), IORESOURCE_MEM)
- #define DEFINE_RES_MEM(_start, _size) \
- DEFINE_RES_MEM_NAMED((_start), (_size), NULL)
- #define DEFINE_RES_NAMED(_start, _size, _name, _flags) \
- { \
- .start = (_start), \
- .end = (_start) + (_size) - 1, \
- .name = (_name), \
- .flags = (_flags), \
- }
- #define DEFINE_RES_MEM_NAMED(_start, _size, _name) \
- DEFINE_RES_NAMED((_start), (_size), (_name), IORESOURCE_MEM)
- #define DEFINE_RES_MEM(_start, _size) \
- DEFINE_RES_MEM_NAMED((_start), (_size), NULL)
- {
- .start = (S3C_PA_NAND),
- .end = (S3C_PA_NAND) + (SZ_1M) - 1,
- .name = (NULL),
- .flags = (IORESOURCE_MEM),
- }
- {
- .start = (S3C_PA_NAND),
- .end = (S3C_PA_NAND) + (SZ_1M) - 1,
- .name = (NULL),
- .flags = (IORESOURCE_MEM),
- }
- #define S3C2410_PA_NAND (0x4E000000)
- #define S3C24XX_PA_NAND S3C2410_PA_NAND
- #define S3C_PA_NAND S3C24XX_PA_NAND
- #define S3C2410_PA_NAND (0x4E000000)
- #define S3C24XX_PA_NAND S3C2410_PA_NAND
- #define S3C_PA_NAND S3C24XX_PA_NAND
也就是说,S3C_PA_NAND是Nand flash寄存器首地址,而SZ_1M明显是个长度,因此,这里的resource实际上是Nand flash寄存器首地址跟接下来的1M空间,可是,Nand的寄存器并没有那么多,这又是为什么呢?这些信息有什么用又在哪里用到了呢?答案很简单,这肯定是给驱动程序使用的了,带着这个疑问我们继续分析代码。定位到/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,
- },
- };
- 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,
- },
- };
- static int __init s3c2410_nand_init(void)
- {
- printk("S3C24XX NAND Driver, (c) 2004 Simtec Electronics\n");
- return platform_driver_register(&s3c24xx_nand_driver);
- }
- static void __exit s3c2410_nand_exit(void)
- {
- platform_driver_unregister(&s3c24xx_nand_driver);
- }
- module_init(s3c2410_nand_init);
- module_exit(s3c2410_nand_exit);
- MODULE_LICENSE("GPL");
- MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
- MODULE_DESCRIPTION("S3C24XX MTD NAND driver");
- static int __init s3c2410_nand_init(void)
- {
- printk("S3C24XX NAND Driver, (c) 2004 Simtec Electronics\n");
- return platform_driver_register(&s3c24xx_nand_driver);
- }
- static void __exit s3c2410_nand_exit(void)
- {
- platform_driver_unregister(&s3c24xx_nand_driver);
- }
- module_init(s3c2410_nand_init);
- module_exit(s3c2410_nand_exit);
- MODULE_LICENSE("GPL");
- MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
- MODULE_DESCRIPTION("S3C24XX MTD NAND driver");
- static int s3c24xx_nand_probe(struct platform_device *pdev)
- {
- struct s3c2410_platform_nand *plat = to_nand_plat(pdev);
- enum s3c_cpu_type cpu_type;
- struct s3c2410_nand_info *info;
- struct s3c2410_nand_mtd *nmtd;
- struct s3c2410_nand_set *sets;
- struct resource *res;
- int err = 0;
- int size;
- int nr_sets;
- int setno;
- cpu_type = platform_get_device_id(pdev)->driver_data;
- pr_debug("s3c2410_nand_probe(%p)\n", pdev);
- info = kzalloc(sizeof(*info), GFP_KERNEL);
- if (info == NULL) {
- dev_err(&pdev->dev, "no memory for flash info\n");
- err = -ENOMEM;
- goto exit_error;
- }
- platform_set_drvdata(pdev, info);
- spin_lock_init(&info->controller.lock);
- init_waitqueue_head(&info->controller.wq);
- /* get the clock source and enable it */
- info->clk = clk_get(&pdev->dev, "nand");
- if (IS_ERR(info->clk)) {
- dev_err(&pdev->dev, "failed to get clock\n");
- err = -ENOENT;
- goto exit_error;
- }
- s3c2410_nand_clk_set_state(info, CLOCK_ENABLE);
- /* allocate and map the resource */
- /* currently we assume we have the one resource */
- res = pdev->resource;
- size = resource_size(res);
- info->area = request_mem_region(res->start, size, pdev->name);
- if (info->area == NULL) {
- dev_err(&pdev->dev, "cannot reserve register region\n");
- err = -ENOENT;
- goto exit_error;
- }
- info->device = &pdev->dev;
- info->platform = plat;
- info->regs = ioremap(res->start, size);
- info->cpu_type = cpu_type;
- if (info->regs == NULL) {
- dev_err(&pdev->dev, "cannot reserve register region\n");
- err = -EIO;
- goto exit_error;
- }
- dev_dbg(&pdev->dev, "mapped registers at %p\n", info->regs);
- /* initialise the hardware */
- err = s3c2410_nand_inithw(info);
- if (err != 0)
- goto exit_error;
- sets = (plat != NULL) ? plat->sets : NULL;
- nr_sets = (plat != NULL) ? plat->nr_sets : 1;
- info->mtd_count = nr_sets;
- /* allocate our information */
- size = nr_sets * sizeof(*info->mtds);
- info->mtds = kzalloc(size, GFP_KERNEL);
- if (info->mtds == NULL) {
- dev_err(&pdev->dev, "failed to allocate mtd storage\n");
- err = -ENOMEM;
- goto exit_error;
- }
- /* initialise all possible chips */
- nmtd = info->mtds;
- for (setno = 0; setno < nr_sets; setno++, nmtd++) {
- pr_debug("initialising set %d (%p, info %p)\n", setno, nmtd, info);
- s3c2410_nand_init_chip(info, nmtd, sets);
- nmtd->scan_res = nand_scan_ident(&nmtd->mtd,
- (sets) ? sets->nr_chips : 1,
- NULL);
- if (nmtd->scan_res == 0) {
- s3c2410_nand_update_chip(info, nmtd);
- nand_scan_tail(&nmtd->mtd);
- s3c2410_nand_add_partition(info, nmtd, sets);
- }
- if (sets != NULL)
- sets++;
- }
- err = s3c2410_nand_cpufreq_register(info);
- if (err < 0) {
- dev_err(&pdev->dev, "failed to init cpufreq support\n");
- goto exit_error;
- }
- if (allow_clk_suspend(info)) {
- dev_info(&pdev->dev, "clock idle support enabled\n");
- s3c2410_nand_clk_set_state(info, CLOCK_SUSPEND);
- }
- pr_debug("initialised ok\n");
- return 0;
- exit_error:
- s3c24xx_nand_remove(pdev);
- if (err == 0)
- err = -EINVAL;
- return err;
- }
- static int s3c24xx_nand_probe(struct platform_device *pdev)
- {
- struct s3c2410_platform_nand *plat = to_nand_plat(pdev);
- enum s3c_cpu_type cpu_type;
- struct s3c2410_nand_info *info;
- struct s3c2410_nand_mtd *nmtd;
- struct s3c2410_nand_set *sets;
- struct resource *res;
- int err = 0;
- int size;
- int nr_sets;
- int setno;
- cpu_type = platform_get_device_id(pdev)->driver_data;
- pr_debug("s3c2410_nand_probe(%p)\n", pdev);
- info = kzalloc(sizeof(*info), GFP_KERNEL);
- if (info == NULL) {
- dev_err(&pdev->dev, "no memory for flash info\n");
- err = -ENOMEM;
- goto exit_error;
- }
- platform_set_drvdata(pdev, info);
- spin_lock_init(&info->controller.lock);
- init_waitqueue_head(&info->controller.wq);
- /* get the clock source and enable it */
- info->clk = clk_get(&pdev->dev, "nand");
- if (IS_ERR(info->clk)) {
- dev_err(&pdev->dev, "failed to get clock\n");
- err = -ENOENT;
- goto exit_error;
- }
- s3c2410_nand_clk_set_state(info, CLOCK_ENABLE);
- /* allocate and map the resource */
- /* currently we assume we have the one resource */
- res = pdev->resource;
- size = resource_size(res);
- info->area = request_mem_region(res->start, size, pdev->name);
- if (info->area == NULL) {
- dev_err(&pdev->dev, "cannot reserve register region\n");
- err = -ENOENT;
- goto exit_error;
- }
- info->device = &pdev->dev;
- info->platform = plat;
- info->regs = ioremap(res->start, size);
- info->cpu_type = cpu_type;
- if (info->regs == NULL) {
- dev_err(&pdev->dev, "cannot reserve register region\n");
- err = -EIO;
- goto exit_error;
- }
- dev_dbg(&pdev->dev, "mapped registers at %p\n", info->regs);
- /* initialise the hardware */
- err = s3c2410_nand_inithw(info);
- if (err != 0)
- goto exit_error;
- sets = (plat != NULL) ? plat->sets : NULL;
- nr_sets = (plat != NULL) ? plat->nr_sets : 1;
- info->mtd_count = nr_sets;
- /* allocate our information */
- size = nr_sets * sizeof(*info->mtds);
- info->mtds = kzalloc(size, GFP_KERNEL);
- if (info->mtds == NULL) {
- dev_err(&pdev->dev, "failed to allocate mtd storage\n");
- err = -ENOMEM;
- goto exit_error;
- }
- /* initialise all possible chips */
- nmtd = info->mtds;
- for (setno = 0; setno < nr_sets; setno++, nmtd++) {
- pr_debug("initialising set %d (%p, info %p)\n", setno, nmtd, info);
- s3c2410_nand_init_chip(info, nmtd, sets);
- nmtd->scan_res = nand_scan_ident(&nmtd->mtd,
- (sets) ? sets->nr_chips : 1,
- NULL);
- if (nmtd->scan_res == 0) {
- s3c2410_nand_update_chip(info, nmtd);
- nand_scan_tail(&nmtd->mtd);
- s3c2410_nand_add_partition(info, nmtd, sets);
- }
- if (sets != NULL)
- sets++;
- }
- err = s3c2410_nand_cpufreq_register(info);
- if (err < 0) {
- dev_err(&pdev->dev, "failed to init cpufreq support\n");
- goto exit_error;
- }
- if (allow_clk_suspend(info)) {
- dev_info(&pdev->dev, "clock idle support enabled\n");
- s3c2410_nand_clk_set_state(info, CLOCK_SUSPEND);
- }
- pr_debug("initialised ok\n");
- return 0;
- exit_error:
- s3c24xx_nand_remove(pdev);
- if (err == 0)
- err = -EINVAL;
- return err;
- }
- res = pdev->resource;
- size = resource_size(res);
- res = pdev->resource;
- size = resource_size(res);
- info->area = request_mem_region(res->start, size, pdev->name);
- if (info->area == NULL) {
- dev_err(&pdev->dev, "cannot reserve register region\n");
- err = -ENOENT;
- goto exit_error;
- }
- info->area = request_mem_region(res->start, size, pdev->name);
- if (info->area == NULL) {
- dev_err(&pdev->dev, "cannot reserve register region\n");
- err = -ENOENT;
- goto exit_error;
- }
- info->regs = ioremap(res->start, size);
- info->regs = ioremap(res->start, size);
继续往下看代码,到for循环处停下来,我们需要注意一下这部分代码,因为我们看到了s3c2410_nand_init_chip,从函数名称上很容易可以看出,这就是Nand的初始化代码,但是这里为什么要使用一个for循环呢?我们看到循环控制变量是nr_sets,往上可以找到
- sets = (plat != NULL) ? plat->sets : NULL;
- nr_sets = (plat != NULL) ? plat->nr_sets : 1;
- sets = (plat != NULL) ? plat->sets : NULL;
- nr_sets = (plat != NULL) ? plat->nr_sets : 1;
- struct s3c2410_platform_nand *plat = to_nand_plat(pdev);
- struct s3c2410_platform_nand *plat = to_nand_plat(pdev);
- static struct s3c2410_platform_nand *to_nand_plat(struct platform_device *dev)
- {
- return dev->dev.platform_data;
- }
- static struct s3c2410_platform_nand *to_nand_plat(struct platform_device *dev)
- {
- return dev->dev.platform_data;
- }
- s3c_nand_set_platdata(&mini2440_nand_info);
- s3c_nand_set_platdata(&mini2440_nand_info);
- /* NAND Flash on MINI2440 board */
- static struct mtd_partition mini2440_default_nand_part[] __initdata = {
- [0] = {
- .name = "u-boot",
- .size = SZ_256K,
- .offset = 0,
- },
- [1] = {
- .name = "u-boot-env",
- .size = SZ_128K,
- .offset = SZ_256K,
- },
- [2] = {
- .name = "kernel",
- /* 5 megabytes, for a kernel with no modules
- * or a uImage with a ramdisk attached */
- .size = 0x00500000,
- .offset = SZ_256K + SZ_128K,
- },
- [3] = {
- .name = "root",
- .offset = SZ_256K + SZ_128K + 0x00500000,
- .size = MTDPART_SIZ_FULL,
- },
- };
- static struct s3c2410_nand_set mini2440_nand_sets[] __initdata = {
- [0] = {
- .name = "nand",
- .nr_chips = 1,
- .nr_partitions = ARRAY_SIZE(mini2440_default_nand_part),
- .partitions = mini2440_default_nand_part,
- .flash_bbt = 1, /* we use u-boot to create a BBT */
- },
- };
- static struct s3c2410_platform_nand mini2440_nand_info __initdata = {
- .tacls = 0,
- .twrph0 = 25,
- .twrph1 = 15,
- .nr_sets = ARRAY_SIZE(mini2440_nand_sets),
- .sets = mini2440_nand_sets,
- .ignore_unset_ecc = 1,
- };
- /* NAND Flash on MINI2440 board */
- static struct mtd_partition mini2440_default_nand_part[] __initdata = {
- [0] = {
- .name = "u-boot",
- .size = SZ_256K,
- .offset = 0,
- },
- [1] = {
- .name = "u-boot-env",
- .size = SZ_128K,
- .offset = SZ_256K,
- },
- [2] = {
- .name = "kernel",
- /* 5 megabytes, for a kernel with no modules
- * or a uImage with a ramdisk attached */
- .size = 0x00500000,
- .offset = SZ_256K + SZ_128K,
- },
- [3] = {
- .name = "root",
- .offset = SZ_256K + SZ_128K + 0x00500000,
- .size = MTDPART_SIZ_FULL,
- },
- };
- static struct s3c2410_nand_set mini2440_nand_sets[] __initdata = {
- [0] = {
- .name = "nand",
- .nr_chips = 1,
- .nr_partitions = ARRAY_SIZE(mini2440_default_nand_part),
- .partitions = mini2440_default_nand_part,
- .flash_bbt = 1, /* we use u-boot to create a BBT */
- },
- };
- static struct s3c2410_platform_nand mini2440_nand_info __initdata = {
- .tacls = 0,
- .twrph0 = 25,
- .twrph1 = 15,
- .nr_sets = ARRAY_SIZE(mini2440_nand_sets),
- .sets = mini2440_nand_sets,
- .ignore_unset_ecc = 1,
- };
(1)初始化了chip中的各种操作函数指针并赋值给了nmtd->mtd.priv。
(2)初始化了info的sel_*成员,显然是Nand片选所用
(3)初始化了nmtd的几个成员
nmtd,info,set是该函数的三个参数,理解了这几个参数也就理解了这个函数的作用。info显然就是s3c24xx_nand_init中的s3c2410_nand_info,nmtd是info->mtds,而info->mtds是kzmalloc开辟的大小为size的内核空间,kzmalloc是kernel zero malloc,也就是开辟了size大小的空间清全部设置为0,也就是nmtds就是空的mtd数组,sets来就前面我定义的mini2440_nand_sets,这样三个参数都知道什么意思了,再去看代码就很简单了。(刚才去打了半小时电话,思路有点乱,不过大体上看了下,这个函数里面没有复杂的操作,相信大家很容易看懂)。
执行完s3c2410_nand_init之后就执行了nand_scan_ident,这是内核函数我就不做分析了,大家自己跟一下就可以知道,这个函数完成了nand_chip其他未指定函数指针的初始化,并获取了Nand的ID信息等,接下来又s3c2410_nand_update_chip,nand_scan_tail,s3c2410_nand_add_partitions,其中nand_scan_tail是通用的内核函数,而s3c2410_nand_update_chip是ecc相关的操作,我们只分析s3c2410_nand_add_partitions,从名字上讲,s3c2410开头的函数肯定不是内核通用函数,也就是说,这实际上是我们需要自行完成的函数,当然,也是可以借鉴的函数,追踪进入s3c2410_nand_add_partitions,看看内核是如何知道分区信息的。
- static int s3c2410_nand_add_partition(struct s3c2410_nand_info *info,
- struct s3c2410_nand_mtd *mtd,
- struct s3c2410_nand_set *set)
- {
- if (set)
- mtd->mtd.name = set->name;
- return mtd_device_parse_register(&mtd->mtd, NULL, NULL,
- set->partitions, set->nr_partitions);
- }
- static int s3c2410_nand_add_partition(struct s3c2410_nand_info *info,
- struct s3c2410_nand_mtd *mtd,
- struct s3c2410_nand_set *set)
- {
- if (set)
- mtd->mtd.name = set->name;
- return mtd_device_parse_register(&mtd->mtd, NULL, NULL,
- set->partitions, set->nr_partitions);
- }
这个函数也很简单,仅设置了下mtd的nand然后就调用和mtd_core.c中的mtd_device_parse_register函数,从参数可以知道,该函数向内核注册了Nand分区信息。这样我们就基本上看完了Linux内核Nand驱动部分的结构。
在结尾之前我还要提到一个问题,就是内核驱动的匹配问题,在platform_device定义时内核指定的名称是s3c2410-nand
- struct platform_device s3c_device_nand = {
- .name = "s3c2410-nand",
- .id = -1,
- .num_resources = ARRAY_SIZE(s3c_nand_resource),
- .resource = s3c_nand_resource,
- };
- struct platform_device s3c_device_nand = {
- .name = "s3c2410-nand",
- .id = -1,
- .num_resources = ARRAY_SIZE(s3c_nand_resource),
- .resource = s3c_nand_resource,
- };
- 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_nand_setname("s3c2440-nand");
- s3c_device_ts.name = "s3c2440-ts";
- s3c_device_usbgadget.name = "s3c2440-usbgadget";
- }
- 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_nand_setname("s3c2440-nand");
- s3c_device_ts.name = "s3c2440-ts";
- s3c_device_usbgadget.name = "s3c2440-usbgadget";
- }
- static struct platform_device_id s3c24xx_driver_ids[] = {
- {
- .name = "s3c2410-nand",
- .driver_data = TYPE_S3C2410,
- }, {
- .name = "s3c2440-nand",
- .driver_data = TYPE_S3C2440,
- }, {
- .name = "s3c2412-nand",
- .driver_data = TYPE_S3C2412,
- }, {
- .name = "s3c6400-nand",
- .driver_data = TYPE_S3C2412, /* compatible with 2412 */
- },
- { }
- };
- static struct platform_device_id s3c24xx_driver_ids[] = {
- {
- .name = "s3c2410-nand",
- .driver_data = TYPE_S3C2410,
- }, {
- .name = "s3c2440-nand",
- .driver_data = TYPE_S3C2440,
- }, {
- .name = "s3c2412-nand",
- .driver_data = TYPE_S3C2412,
- }, {
- .name = "s3c6400-nand",
- .driver_data = TYPE_S3C2412, /* compatible with 2412 */
- },
- { }
- };
- struct bus_type platform_bus_type = {
- .name = "platform",
- .dev_attrs = platform_dev_attrs,
- .match = platform_match,
- .uevent = platform_uevent,
- .pm = &platform_dev_pm_ops,
- };
- int platform_driver_register(struct platform_driver *drv)
- {
- drv->driver.bus = &platform_bus_type;
- if (drv->probe)
- drv->driver.probe = platform_drv_probe;
- if (drv->remove)
- drv->driver.remove = platform_drv_remove;
- if (drv->shutdown)
- drv->driver.shutdown = platform_drv_shutdown;
- return driver_register(&drv->driver);
- }
- struct bus_type platform_bus_type = {
- .name = "platform",
- .dev_attrs = platform_dev_attrs,
- .match = platform_match,
- .uevent = platform_uevent,
- .pm = &platform_dev_pm_ops,
- };
- int platform_driver_register(struct platform_driver *drv)
- {
- drv->driver.bus = &platform_bus_type;
- if (drv->probe)
- drv->driver.probe = platform_drv_probe;
- if (drv->remove)
- drv->driver.remove = platform_drv_remove;
- if (drv->shutdown)
- drv->driver.shutdown = platform_drv_shutdown;
- return driver_register(&drv->driver);
- }
- static int platform_match(struct device *dev, struct device_driver *drv)
- {
- struct platform_device *pdev = to_platform_device(dev);
- struct platform_driver *pdrv = to_platform_driver(drv);
- /* Attempt an OF style match first */
- if (of_driver_match_device(dev, drv))
- return 1;
- /* Then try to match against the id table */
- if (pdrv->id_table)
- return platform_match_id(pdrv->id_table, pdev) != NULL;
- /* fall-back to driver name match */
- return (strcmp(pdev->name, drv->name) == 0);
- }
- static int platform_match(struct device *dev, struct device_driver *drv)
- {
- struct platform_device *pdev = to_platform_device(dev);
- struct platform_driver *pdrv = to_platform_driver(drv);
- /* Attempt an OF style match first */
- if (of_driver_match_device(dev, drv))
- return 1;
- /* Then try to match against the id table */
- if (pdrv->id_table)
- return platform_match_id(pdrv->id_table, pdev) != NULL;
- /* fall-back to driver name match */
- return (strcmp(pdev->name, drv->name) == 0);
- }
- static const struct platform_device_id *platform_match_id(
- const struct platform_device_id *id,
- struct platform_device *pdev)
- {
- while (id->name[0]) {
- if (strcmp(pdev->name, id->name) == 0) {
- pdev->id_entry = id;
- return id;
- }
- id++;
- }
- return NULL;
- }
- static const struct platform_device_id *platform_match_id(
- const struct platform_device_id *id,
- struct platform_device *pdev)
- {
- while (id->name[0]) {
- if (strcmp(pdev->name, id->name) == 0) {
- pdev->id_entry = id;
- return id;
- }
- id++;
- }
- return NULL;
- }
- (1)定义resource,保证可以以物理地址方式正确访问Nand寄存器。(默认有)
- (2)定义platform_device,这是内核记录的硬件信息,要注册到内核设备列表。
- (3)定义mtd_partition,设置Nand分区。
- (4)定义s3c2410_nand_set,枚举所有Nand芯片信息。
- (5)定义s3c2410_platform_nand,这是驱动程序初始化Nand是需要的数据,包括分区信息的和芯片时序等必要信息。
- (6)将s3c2410_platform_nand赋值给mtd_device->dev.platform_data。
- (7)将Nand设备结构注册到设备列表。
- (1)定义resource,保证可以以物理地址方式正确访问Nand寄存器。(默认有)
- (2)定义platform_device,这是内核记录的硬件信息,要注册到内核设备列表。
- (3)定义mtd_partition,设置Nand分区。
- (4)定义s3c2410_nand_set,枚举所有Nand芯片信息。
- (5)定义s3c2410_platform_nand,这是驱动程序初始化Nand是需要的数据,包括分区信息的和芯片时序等必要信息。
- (6)将s3c2410_platform_nand赋值给mtd_device->dev.platform_data。
- (7)将Nand设备结构注册到设备列表。