• 《Linux驱动:nand flash驱动看这一篇就够了》


    一,前言

    nand flash驱动开发总结,涉及到s3c2440芯片nand flash控制器的设置及操作、K9F2G08U0C nand flash的设置及操作、平台总线-驱动-设备模型等相关知识。

    二,硬件电路

    2.1 Nand flash相关

    LDATA0~LDATA7:传输命令、地址和数据。
    RnB:nand flash的工作状态,0表示就绪,1表示正忙。
    CLE:决定DATA0~DATA7传输的是数据还是命令,1为命令,0为数据。
    nFCE:nand flash的片选,1表示选中,0表示未选中(选中才能对其进行操作)。
    ALE:决定DATA0~DATA7传输的是数据还是地址,1为地址,0为数据(CLE和ALE为0)。
    nFWE:为0表示写操作(写命令、地址、数据)。
    nFRE:为0表示读操作。
    在这里插入图片描述

    2.2 S3c2440相关

    根据Nand Flash的芯片书册知,其需要五个字节表示地址,即五个地址周期,其一页的大小为2KB,8位数据/地址传输。根据S3c2440芯片书册可知,需要NCON、GPG13、GPG14为1,GPG15为0,即前者加上拉电阻,后者加下拉电阻。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    2.3 Nand flash 位反转

    由于Nand Flash的固有特性,在读写数据过程中,偶然会产生一位或几位数据错误(这种概率很低),bit位从“1”变为“0”,或者从“1”变为“0”。当位反转发生在关键的代码、数据上时,有可能导致系统崩溃。当仅仅是报告位反转,重新读取即可。如果确实发生了位反转,则必须有相应的错误检测/恢复措施。在NAND Flash上发生位反转的概率很高,推荐使用EDC/ECC进行错误检测和恢复。

    三,Nand flash驱动框架

    在这里插入图片描述

    四,S3c2440 Nand Flash驱动的加载过程

    S3c2440 Nand Flash驱动使用了平台总线-驱动-设备模型。

    4.1 S3c2440 Nand Flash – 设备注册

    linux-2.6.22.6/.config

    CONFIG_ARCH_S3C2440=y
    
    • 1

    linux-2.6.22.6/arch/arm/mach-s3c2440/mach-smdk2440.c

    MACHINE_START(S3C2440, "SMDK2440")
    	/* Maintainer: Ben Dooks  */
    	.phys_io	= S3C2410_PA_UART,
    	.io_pg_offst	= (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
    	.boot_params	= S3C2410_SDRAM_PA + 0x100,
    
    	.init_irq	= s3c24xx_init_irq,
    	.map_io		= smdk2440_map_io,
    	.init_machine	= smdk2440_machine_init,
    	.timer		= &s3c24xx_timer,
    MACHINE_END
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    将上面的宏展开

    static const struct machine_desc __mach_desc_SMDK2440
     __attribute_used__
     __attribute__((__section__(".arch.info.init"))) = {
     .nr = MACH_TYPE_SMDK2410, /* architecture number */
     .name = "SMDK2440", /* architecture name */
     /* Maintainer: Jonas Dietsche */
     .phys_io = S3C2410_PA_UART, /* start of physical io */
     .io_pg_offst = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
     .boot_params = S3C2410_SDRAM_PA + 0x100, /* tagged list */
     .map_io = smdk2440_map_io, /* IO mapping function */
     .init_irq = s3c24xx_init_irq,
     .init_machine = smdk2440_machine_init,
     .timer = &s3c24xx_timer,
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    MACHINE_START主要是定义了"struct machine_desc"的类型,放在 section(“.arch.info.init”),是初始化数据,Kernel 起来之后将被丢弃。
    各个成员函数在不同时期被调用:

    1. .init_machine 在 arch/arm/kernel/setup.c 中被 customize_machine 调用,放在 arch_initcall() 段里面,会自动按顺序被调用。
    2. init_irq在start_kernel() -> init_IRQ() -> init_arch_irq() 被调用
    3. map_io 在 setup_arch() -> paging_init() -> devicemaps_init()被调用
      其他主要都在 setup_arch() 中用到。

    系统初始化时,会调用smdk2440_machine_init

    // linux-2.6.22.6/arch/arm/mach-s3c2440/mach-smdk2440.c
    static void __init smdk2440_machine_init(void)
    {
    	s3c24xx_fb_set_platdata(&smdk2440_lcd_cfg);
    
    	platform_add_devices(smdk2440_devices, ARRAY_SIZE(smdk2440_devices));
    	smdk_machine_init();
    }
    
    // linux-2.6.22.6/arch/arm/plat-s3c24xx/common-smdk.c
    void __init smdk_machine_init(void)
    {
    	......
        // 配置nand flash
        s3c_device_nand.dev.platform_data = &smdk_nand_info;
        ......
        // 注册到平台总线
    	platform_add_devices(smdk_devs, ARRAY_SIZE(smdk_devs));
    	......
    }
    
    // 配置了nand flash各信号脉冲宽度或时间间隔,以及分区
    static struct s3c2410_platform_nand smdk_nand_info = {
    	.tacls		= 20,
    	.twrph0		= 60,
    	.twrph1		= 20,
    	.nr_sets	= ARRAY_SIZE(smdk_nand_sets),
    	.sets		= smdk_nand_sets,
    };
    // 设置nand flash的分区
    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,
    	},
    };
    // 配置nand flash的具体分区,四个分区
    static struct mtd_partition smdk_default_nand_part[] = {
    	[0] = {
            .name   = "bootloader",
            .size   = 0x00040000,
    		.offset	= 0,
    	},
    	[1] = {
            .name   = "params",
            .offset = MTDPART_OFS_APPEND,
            .size   = 0x00020000,
    	},
    	[2] = {
            .name   = "kernel",
            .offset = MTDPART_OFS_APPEND,
            .size   = 0x00200000,
    	},
    	[3] = {
            .name   = "root",
            .offset = MTDPART_OFS_APPEND,
            .size   = MTDPART_SIZ_FULL,
    	}
    };
    
    // linux-2.6.22.6/arch/arm/plat-s3c24xx/common-smdk.c
    static struct platform_device __initdata *smdk_devs[] = {
    	&s3c_device_nand,
    	......
    };
    
    // linux-2.6.22.6/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,
    	}
    };
    
    // 将s3c_device_nand设备注册到平台总线
    // linux-2.6.22.6/drivers/base/platform.c
    int platform_add_devices(struct platform_device **devs, int num)
    {
    	int i, ret = 0;
    
    	for (i = 0; i < num; i++) {
    		ret = platform_device_register(devs[i]);
    		if (ret) {
    			while (--i >= 0)
    				platform_device_unregister(devs[i]);
    			break;
    		}
    	}
    
    	return ret;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101

    4.2 S3c2440 Nand Flash – 驱动注册

    make menuconfig,将s3c2440 nand flash驱动加载到内核中,系统启动时便会自动加载驱动。

    
    -> Device Drivers                                                                                               │   
      │       -> Memory Technology Device (MTD) support (MTD [=y])                                                          │   
      │         -> NAND Device Support (MTD_NAND [=y])  
                      │ │     <*>   NAND Flash support for S3C2410/S3C2440 SoC                                            │ │   
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    加载驱动,调用驱动初始化函数–s3c2410_nand_init
    这里注册了三个nand flash驱动到平台总线。会根据注册到平台总线的nand flash设备进行匹配,选择使用哪个驱动。

    static int __init s3c2410_nand_init(void)
    {
    	printk("S3C24XX NAND Driver, (c) 2004 Simtec Electronics\n");
    
    	platform_driver_register(&s3c2412_nand_driver);
    	platform_driver_register(&s3c2440_nand_driver);
    	return platform_driver_register(&s3c2410_nand_driver);
    }
    
    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,
    	},
    };
    
    static struct platform_driver s3c2440_nand_driver = {
    	.probe		= s3c2440_nand_probe,
    	.remove		= s3c2410_nand_remove,
    	.suspend	= s3c24xx_nand_suspend,
    	.resume		= s3c24xx_nand_resume,
    	.driver		= {
    		.name	= "s3c2440-nand",
    		.owner	= THIS_MODULE,
    	},
    };
    
    static struct platform_driver s3c2412_nand_driver = {
    	.probe		= s3c2412_nand_probe,
    	.remove		= s3c2410_nand_remove,
    	.suspend	= s3c24xx_nand_suspend,
    	.resume		= s3c24xx_nand_resume,
    	.driver		= {
    		.name	= "s3c2412-nand",
    		.owner	= THIS_MODULE,
    	},
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    4.3 S3c2440 Nand Flash – 设备-驱动匹配

    4.3.1 设备注册到平台总线时的匹配

    platform_add_devices->
        platform_device_register->
            platform_device_add->
                device_add->
                    bus_attach_device(dev)->
                        device_attach(dev)->
                            // 因为是注册到平台总线,
        					// 所以从平台总线的驱动链表中取出每一个驱动和该设备进行匹配
                            bus_for_each_drv(dev->bus, NULL, dev, __device_attach)->
                                __device_attach->
                                    driver_probe_device(drv, dev)->	
                                        // 因为是注册到平台总线,调用平台总线的匹配函数,
        								// 即platform_match
                                        if (drv->bus->match && !drv->bus->match(dev, drv))
    
    // 平台总线的匹配函数,通过比较驱动和设备的名称进行匹配                                       
    static int platform_match(struct device * dev, struct device_driver * drv)
    {
    	struct platform_device *pdev = container_of(dev, struct platform_device, dev);
    
    	return (strncmp(pdev->name, drv->name, BUS_ID_SIZE) == 0);
    }                 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    4.3.2 驱动注册到平台总线时的匹配

    platform_driver_register->
        driver_register->
            bus_add_driver->
                driver_attach->
                    // 因为是注册到平台总线,
        			// 所以从平台总线的设备链表中取出每一个设备和该驱动进行匹配
                    bus_for_each_dev(drv->bus, NULL, drv, __driver_attach)->
                        __driver_attach->
                            driver_probe_device->
                                // 因为是注册到平台总线,调用平台总线的匹配函数,
                                // 即platform_match
                                if (drv->bus->match && !drv->bus->match(dev, drv))
    
    // 平台总线的匹配函数,通过比较驱动和设备的名称进行匹配                                       
    static int platform_match(struct device * dev, struct device_driver * drv)
    {
    	struct platform_device *pdev = container_of(dev, struct platform_device, dev);
    
    	return (strncmp(pdev->name, drv->name, BUS_ID_SIZE) == 0);
    }                 
                    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    4.4 S3c2440 Nand Flash – 调用驱动的probe函数

    int driver_probe_device(struct device_driver * drv, struct device * dev)
    {
    	int ret = 0;
    
    	if (!device_is_registered(dev))
    		return -ENODEV;
    	if (drv->bus->match && !drv->bus->match(dev, drv))
    		goto done;
    
    	pr_debug("%s: Matched Device %s with Driver %s\n",
    		 drv->bus->name, dev->bus_id, drv->name);
    
    	ret = really_probe(dev, drv);
    
    done:
    	return ret;
    }
    
    // if (drv->bus->match && !drv->bus->match(dev, drv)) 匹配成功后调用really_probe函数
    really_probe->
        // 因为注册的是平台总线,所以看平台总线是否具有probe函数
        // 如果平台总线具有probe函数则调用平台总线的probe函数,否则直接调用驱动的probe函数
        if (dev->bus->probe) {
    		ret = dev->bus->probe(dev);
    		if (ret)
    			goto probe_failed;
    	} else if (drv->probe) {
    		ret = drv->probe(dev);
    		if (ret)
    			goto probe_failed;
    	}
    // 平台总线具有probe函数platform_drv_probe
    static int platform_drv_probe(struct device *_dev)
    {
    	struct platform_driver *drv = to_platform_driver(_dev->driver);
    	struct platform_device *dev = to_platform_device(_dev);
    	// 调用驱动的probe函数
    	return drv->probe(dev);
    }
    
    // 设备
    struct platform_device s3c_device_nand = {
    	.name		  = "s3c2410-nand",
    	.id		  = -1,
    	.num_resources	  = ARRAY_SIZE(s3c_nand_resource),
    	.resource	  = s3c_nand_resource,
    };
    // 驱动
    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,
    	},
    };
    
    // 通过名称两者匹配,即调用s3c2410_nand_probe
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60

    五,驱动的probe函数分析

    static int s3c2410_nand_probe(struct platform_device *dev)
    {
    	return s3c24xx_nand_probe(dev, TYPE_S3C2410);
    }
    
    • 1
    • 2
    • 3
    • 4

    5.1 使能时钟

    info->clk = clk_get(&pdev->dev, "nand");
    if (IS_ERR(info->clk)) {
        dev_err(&pdev->dev, "failed to get clock");
        err = -ENOENT;
        goto exit_error;
    }
    clk_enable(info->clk);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    5.2 端口映射

    在这里插入图片描述

    #define S3C2410_PA_NAND	   (0x4E000000)
    #define S3C24XX_SZ_NAND	   SZ_1M
    static struct resource s3c_nand_resource[] = {
    	[0] = {
    		.start = S3C2410_PA_NAND,
    		.end   = S3C2410_PA_NAND + S3C24XX_SZ_NAND - 1,
    		.flags = IORESOURCE_MEM,
    	}
    };
    res  = pdev->resource;
    size = res->end - res->start + 1;
    
    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);
    if (info->regs == NULL) {
    		dev_err(&pdev->dev, "cannot reserve register region\n");
    		err = -EIO;
    		goto exit_error;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    5.3 s3c2440 nand flash控制器设置

    5.3.1 设置脉冲宽度和间隔

    脉冲宽度和信号间隔

    static int s3c2410_nand_inithw(struct s3c2410_nand_info *info,
    			       struct platform_device *pdev)
    {
    	struct s3c2410_platform_nand *plat = to_nand_plat(pdev);
    	unsigned long clkrate = clk_get_rate(info->clk);
    	int tacls_max = (info->cpu_type == TYPE_S3C2412) ? 8 : 4;
    	int tacls, twrph0, twrph1;
    	unsigned long cfg = 0;
    
    	/* calculate the timing information for the controller */
    
    	clkrate /= 1000;	/* turn clock into kHz for ease of use */
    
    	if (plat != NULL) {
    		tacls = s3c_nand_calc_rate(plat->tacls, clkrate, tacls_max);
    		twrph0 = s3c_nand_calc_rate(plat->twrph0, clkrate, 8);
    		twrph1 = s3c_nand_calc_rate(plat->twrph1, clkrate, 8);
    	} else {
    		/* default timings */
    		tacls = tacls_max;
    		twrph0 = 8;
    		twrph1 = 8;
    	}
    
    	if (tacls < 0 || twrph0 < 0 || twrph1 < 0) {
    		dev_err(info->device, "cannot get suitable timings\n");
    		return -EINVAL;
    	}
    
    	dev_info(info->device, "Tacls=%d, %dns Twrph0=%d %dns, Twrph1=%d %dns\n",
    	       tacls, to_ns(tacls, clkrate), twrph0, to_ns(twrph0, clkrate), twrph1, to_ns(twrph1, clkrate));
    
     	switch (info->cpu_type) {
     	case TYPE_S3C2410:
    		cfg = S3C2410_NFCONF_EN;
    		cfg |= S3C2410_NFCONF_TACLS(tacls - 1);
    		cfg |= S3C2410_NFCONF_TWRPH0(twrph0 - 1);
    		cfg |= S3C2410_NFCONF_TWRPH1(twrph1 - 1);
    		break;
    
     	case TYPE_S3C2440:
     	case TYPE_S3C2412:
    		cfg = S3C2440_NFCONF_TACLS(tacls - 1);
    		cfg |= S3C2440_NFCONF_TWRPH0(twrph0 - 1);
    		cfg |= S3C2440_NFCONF_TWRPH1(twrph1 - 1);
    
    		/* enable the controller and de-assert nFCE */
        	// 使能 nand flash控制器
    		writel(S3C2440_NFCONT_ENABLE, info->regs + S3C2440_NFCONT);
    	}
    
    	dev_dbg(info->device, "NF_CONF is 0x%lx\n", cfg);
    
    	writel(cfg, info->regs + S3C2410_NFCONF);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56

    5.4 设置struct nand_chip

    static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,
    				   struct s3c2410_nand_mtd *nmtd,
    				   struct s3c2410_nand_set *set)
    {
    	struct nand_chip *chip = &nmtd->chip;
    	void __iomem *regs = info->regs;
    
    	// 提供nand flash读写数据接口
    	chip->write_buf    = s3c2410_nand_write_buf;
    	chip->read_buf     = s3c2410_nand_read_buf;
    	// 提供nand flash 片选接口
    	chip->select_chip  = s3c2410_nand_select_chip;
    	chip->chip_delay   = 50;
    	chip->priv	   = nmtd;
    	chip->options	   = 0;
    	chip->controller   = &info->controller;
    
    	switch (info->cpu_type) {
    	case TYPE_S3C2410:
    		// 提供 nand flash控制器数据写入的寄存器地址
    		chip->IO_ADDR_W = regs + S3C2410_NFDATA;
    		info->sel_reg   = regs + S3C2410_NFCONF;
    		info->sel_bit	= S3C2410_NFCONF_nFCE;
    		// 提供nand flash 写命令或者地址接口
    		chip->cmd_ctrl  = s3c2410_nand_hwcontrol;
    		// 提供nand flash 判断就绪或忙状态接口
    		chip->dev_ready = s3c2410_nand_devready;
    		break;
    
    	......
    
    
    		if (readl(regs + S3C2410_NFCONF) & S3C2412_NFCONF_NANDBOOT)
    			dev_info(info->device, "System booted from NAND\n");
    
    		break;
      	}
    
    	// 提供 nand flash控制器数据读取的寄存器地址
    	chip->IO_ADDR_R = chip->IO_ADDR_W;
    
    	nmtd->info	   = info;
    	nmtd->mtd.priv	   = chip;
    	nmtd->mtd.owner    = THIS_MODULE;
        // 设置分区信息
    	nmtd->set	   = set;
    	// 使用硬件ecc校验还是软件ecc校验,以解决位反转问题。这里使用软件ecc
    	if (hardware_ecc) {
    
    		......
    
    
    	} else {
    		chip->ecc.mode	    = NAND_ECC_SOFT;
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56

    5.5 nand_scan流程分析

    nand_scan(&nmtd->mtd, (sets) ? sets->nr_chips : 1) ->
        nand_scan_ident(mtd, maxchips) ->
            // 设置默认的nand flash接口,
        	// 比如设置struct nand_chip时提供的nand flash读写数据接口等
            // 像5.4小节事先提供了则使用提供的,否则使用默认的
            nand_set_defaults(chip, busw);
            // 从nand flash中读出其设备id ,和nand_flash_ids数组中定义的各型号flash信息对比,
        	// 得到其flash型号。比如读到K9F2G08U0C nand flash的设备Id为da,
            // 则从nand_flash_ids数组中可得其信息为
    		// {"NAND 256MiB 3,3V 8-bit",	0xDA, 0, 256, 0, LP_OPTIONS},
            nand_get_flash_type(mtd, chip, busw, &nand_maf_id);
    	// 填充所有未初始化的函数指针(比如nand_erase等接口),并在适当的情况下扫描坏的块表。
    	nand_scan_tail(mtd);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    5.6 添加分区

    5.6.1 遍历mtd_notifiers,通过其add接口添加分区

    s3c2410_nand_add_partition ->
        // 在s3c2410_nand_init_chip函数中已经将分区信息设置给了mtd 
        add_mtd_device(&mtd->mtd) ->
            // 遍历mtd_notifiers,通过其add接口添加分区
            list_for_each(this, &mtd_notifiers) {
    				struct mtd_notifier *not = list_entry(this, struct mtd_notifier, list);
    				not->add(mtd);
    			}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    5.6.2 设置mtd_notifiers链表

    5.6.2.1 nand flash 字符设备
    // linux-2.6.22.6/drivers/mtd/mtdchar.c
    init_mtdchar ->	
        register_mtd_user(&notifier) ->
            list_add(&new->list, &mtd_notifiers);
    
    static struct mtd_notifier notifier = {
    	.add	= mtd_notify_add,
    	.remove	= mtd_notify_remove,
    };
    
    static void mtd_notify_add(struct mtd_info* mtd)
    {
    	if (!mtd)
    		return;
    	// 创建字符设备 设备节点为/dev/mtd%d
    	class_device_create(mtd_class, NULL, MKDEV(MTD_CHAR_MAJOR, mtd->index*2),
    			    NULL, "mtd%d", mtd->index);
    	// 创建字符设备 设备节点为/dev/mtd%dro (只读)
    	class_device_create(mtd_class, NULL,
    			    MKDEV(MTD_CHAR_MAJOR, mtd->index*2+1),
    			    NULL, "mtd%dro", mtd->index);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    5.6.2.2 nand flash的块设备
    // linux-2.6.22.6/drivers/mtd/mtd_blkdevs.c
    register_mtd_blktrans->
        register_mtd_user(&blktrans_notifier) ->
            list_add(&new->list, &mtd_notifiers);
    
    static struct mtd_notifier blktrans_notifier = {
    	.add = blktrans_notify_add,
    	.remove = blktrans_notify_remove,
    };
    
    static void blktrans_notify_add(struct mtd_info *mtd)
    {
    	struct list_head *this;
    
    	if (mtd->type == MTD_ABSENT)
    		return;
    
    	list_for_each(this, &blktrans_majors) {
    		struct mtd_blktrans_ops *tr = list_entry(this, struct mtd_blktrans_ops, list);
    
    		tr->add_mtd(tr, mtd);
    	}
    
    }
    
    blktrans_notify_add ->
        // 遍历blktrans_majors 通过其add_mtd函数添加分区
        list_for_each(this, &blktrans_majors) {
    		struct mtd_blktrans_ops *tr = list_entry(this, struct mtd_blktrans_ops, list);
    		tr->add_mtd(tr, mtd);
    	}
    
    
    // 设置blktrans_majors链表 两个地方设置,一个mtdblock_ro.c 一个mtdblock.c
    
    // linux-2.6.22.6/drivers/mtd/mtdblock_ro.c
    mtdblock_init->
        register_mtd_blktrans(&mtdblock_tr) -> 
            list_add(&tr->list, &blktrans_majors);
    
    static struct mtd_blktrans_ops mtdblock_tr = {
    	.name		= "mtdblock",
    	.major		= 31,
    	.part_bits	= 0,
    	.blksize 	= 512,
    	.readsect	= mtdblock_readsect,
    	.writesect	= mtdblock_writesect,
    	.add_mtd	= mtdblock_add_mtd,
    	.remove_dev	= mtdblock_remove_dev,
    	.owner		= THIS_MODULE,
    };
    
    mtdblock_add_mtd ->
        add_mtd_blktrans_dev(dev);
    		alloc_disk(1 << tr->part_bits);
        	set_capacity(gd, (new->size * tr->blksize) >> 9);
        	add_disk(gd);
    
    // linux-2.6.22.6/drivers/mtd/mtdblock.c
    init_mtdblock -> 
        register_mtd_blktrans(&mtdblock_tr);
        	list_add(&tr->list, &blktrans_majors);
    
    static struct mtd_blktrans_ops mtdblock_tr = {
    	.name		= "mtdblock",
    	.major		= 31,
    	.part_bits	= 0,
    	.blksize 	= 512,
    	.open		= mtdblock_open,
    	.flush		= mtdblock_flush,
    	.release	= mtdblock_release,
    	.readsect	= mtdblock_readsect,
    	.writesect	= mtdblock_writesect,
    	.add_mtd	= mtdblock_add_mtd,
    	.remove_dev	= mtdblock_remove_dev,
    	.owner		= THIS_MODULE,
    };
    
    mtdblock_add_mtd -> 
        add_mtd_blktrans_dev(dev)->
            alloc_disk(1 << tr->part_bits);
        	set_capacity(gd, (new->size * tr->blksize) >> 9);
        	add_disk(gd);
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84

    5.6.3 块设备初始化请求队列

    // linux-2.6.22.6/drivers/mtd/mtdblock.c
    init_mtdblock -> 
        register_mtd_blktrans(&mtdblock_tr);
        	tr->blkcore_priv->rq = blk_init_queue(mtd_blktrans_request, &tr->blkcore_priv->queue_lock);
    
    // linux-2.6.22.6/drivers/mtd/mtdblock_ro.c
    mtdblock_init->
        register_mtd_blktrans(&mtdblock_tr) -> 
            tr->blkcore_priv->rq = blk_init_queue(mtd_blktrans_request, &tr->blkcore_priv->queue_lock);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    六,总结

    6.1 nand flash字符设备创建过程

    系统启动,内核初始化时加载mtdchar模块,调用init_mtdchar,将 struct mtd_notifier 结构注册到mtd_notifiers链表中,供后续nand flash驱动程序使用。

    // linux-2.6.22.6/drivers/mtd/mtdchar.c
    init_mtdchar ->	
        register_mtd_user(&notifier) ->
            list_add(&new->list, &mtd_notifiers);
    
    static struct mtd_notifier notifier = {
    	.add	= mtd_notify_add,
    	.remove	= mtd_notify_remove,
    };
    
    static void mtd_notify_add(struct mtd_info* mtd)
    {
    	if (!mtd)
    		return;
    	// 创建字符设备 设备节点为/dev/mtd%d
    	class_device_create(mtd_class, NULL, MKDEV(MTD_CHAR_MAJOR, mtd->index*2),
    			    NULL, "mtd%d", mtd->index);
    	// 创建字符设备 设备节点为/dev/mtd%dro (只读)
    	class_device_create(mtd_class, NULL,
    			    MKDEV(MTD_CHAR_MAJOR, mtd->index*2+1),
    			    NULL, "mtd%dro", mtd->index);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    nand flash驱动程序,调用s3c2410_nand_add_partition接口添加分区时,调用struct mtd_notifier结构中的add函数添加分区,创建字符设备节点。

    s3c2410_nand_add_partition ->
        // 在s3c2410_nand_init_chip函数中已经将分区信息设置给了mtd 
        add_mtd_device(&mtd->mtd) ->
            // 遍历mtd_notifiers,通过其add接口添加分区
            list_for_each(this, &mtd_notifiers) {
    				struct mtd_notifier *not = list_entry(this, struct mtd_notifier, list);
    				not->add(mtd);
    			}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    6.2 nand flash块设备创建过程

    系统启动,内核初始化时加载mtdblock模块,调用init_mtdblock,将struct mtd_notifier 结构注册到mtd_notifiers链表中,注册块设备,初始化请求队列,注册struct mtd_blktrans_ops结构到blktrans_majors链表,供后续nand flash驱动程序使用。(mtdblock_ro.c 和 mtdblock.c类似操作)

    init_mtdblock -> 
        register_mtd_blktrans(&mtdblock_tr) ->
            // 将struct mtd_notifier 结构注册到mtd_notifiers链表中
            if (!blktrans_notifier.list.next)
    			register_mtd_user(&blktrans_notifier);
        	// 注册块设备 /dev/mtdblock
        	ret = register_blkdev(tr->major, tr->name);
            // 初始化请求队列
        	tr->blkcore_priv->rq = blk_init_queue(mtd_blktrans_request, &tr->blkcore_priv->queue_lock);
            // 注册struct mtd_blktrans_ops结构到blktrans_majors链表
    		list_add(&tr->list, &blktrans_majors);
    
    
    static struct mtd_notifier blktrans_notifier = {
    	.add = blktrans_notify_add,
    	.remove = blktrans_notify_remove,
    };
    
    static void blktrans_notify_add(struct mtd_info *mtd)
    {
    	struct list_head *this;
    
    	if (mtd->type == MTD_ABSENT)
    		return;
    
    	list_for_each(this, &blktrans_majors) {
    		struct mtd_blktrans_ops *tr = list_entry(this, struct mtd_blktrans_ops, list);
    
    		tr->add_mtd(tr, mtd);
    	}
    
    }
    
    static struct mtd_blktrans_ops mtdblock_tr = {
    	.name		= "mtdblock",
    	.major		= 31,
    	.part_bits	= 0,
    	.blksize 	= 512,
    	.open		= mtdblock_open,
    	.flush		= mtdblock_flush,
    	.release	= mtdblock_release,
    	.readsect	= mtdblock_readsect,
    	.writesect	= mtdblock_writesect,
    	.add_mtd	= mtdblock_add_mtd,
    	.remove_dev	= mtdblock_remove_dev,
    	.owner		= THIS_MODULE,
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    nand flash驱动程序,调用s3c2410_nand_add_partition接口添加分区时,调用struct mtd_notifier结构中的add函数添加分区,调用struct mtd_notifier结构中的add函数(blktrans_notify_add),即调用struct mtd_blktrans_ops结构中的add_mtd函数(mtdblock_add_mtd)。

    s3c2410_nand_add_partition ->
        // 在s3c2410_nand_init_chip函数中已经将分区信息设置给了mtd 
        add_mtd_device(&mtd->mtd) ->
            // 遍历mtd_notifiers,通过其add接口添加分区
            list_for_each(this, &mtd_notifiers) {
    				struct mtd_notifier *not = list_entry(this, struct mtd_notifier, list);
    				not->add(mtd);
    			}
    
    blktrans_notify_add -> 
        mtdblock_add_mtd -> 
        add_mtd_blktrans_dev(dev)->
            alloc_disk(1 << tr->part_bits);
        	set_capacity(gd, (new->size * tr->blksize) >> 9);
        	add_disk(gd);
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    6.3 一次应用层读取Nand Flash数据的过程

    // 对块设备节点/dev/mtdblock进行访问操作
    
    //一个读数据的bio,被合并或被生成一个请求,触发请求队列的请求处理函数
    mtd_blktrans_request ->
        // 唤醒一个休眠线程--mtd_blktrans_thread,该线程在register_mtd_blktrans中启动
    	wake_up_process(tr->blkcore_priv->thread);
    
    // 该线程从请求队列中取出一个请求调用do_blktrans_request接口进行处理
    mtd_blktrans_thread
        do_blktrans_request -> 
            //tr->readsect 即 struct mtd_blktrans_ops 中的 mtdblock_readsect接口
            tr->readsect(dev, block, buf) ->
                do_cached_read(mtdblk, block<<9, 512, buf) -> 
                    //mtd->read 即 struct mtd_info中的 nand_read接口
                    //在 nand_scan_tail接口中被设置 mtd->read = nand_read;
                    mtd->read(mtd, pos, size, &retlen, buf) ->
                        nand_do_read_ops(mtd, from, &chip->ops) ->
                            chip->ecc.read_page_raw(mtd, chip, bufpoi) ->
                                nand_read_page_raw -> 
                                    // chip->read_buf 即 s3c2410_nand_read_buf
        							// 在驱动程序中 s3c2410_nand_init_chip函数里设置
        							// chip->read_buf = s3c2410_nand_read_buf;
                                    chip->read_buf(mtd, buf, mtd->writesize); 
        							chip->read_buf(mtd, chip->oob_poi, mtd->oobsize) ->
                                        // 从nand flash控制器的NFDATA寄存器中读取数据
                                        // chip->IO_ADDR_W = regs + S3C2410_NFDATA;
                                        // chip->IO_ADDR_R = chip->IO_ADDR_W;
                                        readsb(this->IO_ADDR_R, buf, len);                
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
  • 相关阅读:
    外卖项目(SpringBoot)--- 项目优化 [读写分离、Nginx使用]
    用户忠诚度衡量指标丨利用净推荐值减少流失
    制造不可靠的系统
    娄底医药工业洁净厂建设基本要点概述
    flutter dio 网络封装。记录
    【雅思阅读】王希伟阅读P4(matching2段落信息配对题【困难】)
    覆盖接入2w+交通监测设备,EMQ为深圳市打造交通全要素数字化新引擎
    Js实现前端分页器
    带妹妹学密码系列二(基础知识篇)
    前端内容记录之常用置标
  • 原文地址:https://blog.csdn.net/qq_40709487/article/details/126808923