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

这篇具有很好参考价值的文章主要介绍了《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表示读操作。
《Linux驱动:nand flash驱动看这一篇就够了》

2.2 S3c2440相关

根据Nand Flash的芯片书册知,其需要五个字节表示地址,即五个地址周期,其一页的大小为2KB,8位数据/地址传输。根据S3c2440芯片书册可知,需要NCON、GPG13、GPG14为1,GPG15为0,即前者加上拉电阻,后者加下拉电阻。
《Linux驱动:nand flash驱动看这一篇就够了》
《Linux驱动:nand flash驱动看这一篇就够了》
《Linux驱动:nand flash驱动看这一篇就够了》

2.3 Nand flash 位反转

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

三,Nand flash驱动框架

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

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

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

4.1 S3c2440 Nand Flash – 设备注册

linux-2.6.22.6/.config

CONFIG_ARCH_S3C2440=y

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

MACHINE_START(S3C2440, "SMDK2440")
	/* Maintainer: Ben Dooks <ben@fluff.org> */
	.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

将上面的宏展开

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,
}

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;
}

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                                            │ │   

加载驱动,调用驱动初始化函数–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,
	},
};

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);
}                 

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);
}                 
                

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

五,驱动的probe函数分析

static int s3c2410_nand_probe(struct platform_device *dev)
{
	return s3c24xx_nand_probe(dev, TYPE_S3C2410);
}

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);

5.2 端口映射

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

#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;
}

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;
}

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;
	}
}

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);

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);
			}

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);
}
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);

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);

六,总结

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);
}

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);
			}

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,
};

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)。文章来源地址https://www.toymoban.com/news/detail-410029.html

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);

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);                

到了这里,关于《Linux驱动:nand flash驱动看这一篇就够了》的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Linux】shell编程基础(超详细,入门看这一篇就够了)

    🥇🥇【Liunx学习记录篇】🥇🥇 篇一:【Linux】VMware安装unbuntu18.04虚拟机-超详细步骤(附镜像文件) 篇二:【Linux】ubuntu18.04系统基础配置及操作 篇三:【Linux】用户与组的操作详细介绍 篇四:【Linux】管理Linux文件权限属性介绍 篇五:【Linux】使用数字表示法和文件表示法修

    2024年02月04日
    浏览(48)
  • 还不会在Linux上面如何部署jar包?看这一篇就够了

    1、用WINSCP工具把对应的jar包上传到linux服务器指定位置 2、利用putty工具连接服务器,输入 cd / 命令切换到根目录; cd home/ 切换到jar包目录,我是放在了home目录下,这个要切换到对应的目录。 ll 命令查看目录下文件 3、输入命令启动jar包 一、后台启动,关闭当前窗口程序继续

    2024年02月12日
    浏览(39)
  • 1秒将本地SpringBoot项目jar包部署到Linux环境(看这一篇就够了)

    在IDEA编辑器中 一键 将本地已经打包好的 SpringBoot 项目部署到 linux 环境,实现 秒级 部署 前置知识:需掌握Maven的打包 在windows环境使用命令启动SpringBoot项目 在Linux环境使用命令启动项目 在Linux环境使用脚本启动项目 优化脚本,每次启动时先关闭旧项目 分离依赖,每次上传

    2024年02月04日
    浏览(57)
  • CSS基础——看这一篇就够了

    目录 一、CSS简介 1.CSS是什么? 2.CSS的作用 3.CSS的构成 二、CSS选择器 1.基础选择器 (1).标签选择器 (2)类选择器 (3)标签选择器 (4) 通配符选择器 2.复合选择器 (1)后代选择器(包含选择器) (2)子选择器 (3)并集选择器 (4)伪类选择器  三、基本属性 1.字体属性

    2024年02月09日
    浏览(71)
  • 精通线程池,看这一篇就够了

    当我们运用多线程技术处理任务时,需要不断通过new的方式创建线程,这样频繁创建和销毁线程,会造成cpu消耗过多。那么有没有什么办法 避免频繁创建线程 呢? 当然有,和我们以前学习过多连接池技术类似,线程池通过提前创建好线程保存在线程池中, 在任务要执行时取

    2023年04月17日
    浏览(96)
  • 还不会拓扑排序?看这一篇就够了

    拓扑排序是一种有向无环图(DAG)的顶点排序方法,它将一个有向无环图中的所有顶点排成一个线性序列,使得图中 任意一条有向边上的起点排在终点的前面 。 这样说还不够具体,我们先来看一个例子。假设某大学的课程安排如下: 课程编号 课程名称 先修课程 1 1 1 高等数

    2023年04月08日
    浏览(110)
  • 还不会二分查找?看这一篇就够了

    二分查找分为整数二分和浮点数二分,一般所说的二分查找都是指整数二分。 满足单调性的数组一定可以使用二分查找,但可以使用二分查找的数组不一定需要满足单调性。 不妨假设我们找到了条件 C 1 C_1 C 1 ​ ,它和它的 对立条件 C 2 C_2 C 2 ​ 能够将数组 a a a 一分为二,

    2024年01月19日
    浏览(52)
  • SourceTree使用看这一篇就够了

     你梦想有一天成为git大师,然而面对复杂的git命令,你感觉TMD这我能记得住吗?你曾经羡慕从命令行敲git命令,才会更加炫酷,然而时间一长,TMD命令我有忘了。那么今天我介绍的这款工具会让你从git命令中解救出来,这就是git可视化工具SourcTree。 事实上Git的功能十分强大

    2024年02月08日
    浏览(64)
  • Docker Volume 看这一篇就够了

    默认情况下,在容器内创建的所有文件都存储在可写容器层上。这意味着: 当该容器不再存在时,数据不会持续存在,并且如果另一个进程需要数据,则可能很难将数据从容器中取出。 容器的可写层与运行容器的主机紧密耦合。您无法轻松地将数据移动到其他地方。 写入容

    2024年02月02日
    浏览(97)
  • CAS自旋锁,看这一篇就够了

    前序 时隔多年,杰伦终于出了新专辑,《最伟大的作品》让我们穿越到1920年,见到了马格利特的绿苹果、大利的超现实、常玉画的大腿、莫奈的睡莲、徐志摩的诗… 他说“最伟大的作品”并不是自己的歌,而是这个世界上最伟大的艺术作品们。 为什么要写CAS自旋锁呢?最近

    2023年04月08日
    浏览(45)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包