国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

《Linux驅(qū)動(dòng):nand flash驅(qū)動(dòng)看這一篇就夠了》

這篇具有很好參考價(jià)值的文章主要介紹了《Linux驅(qū)動(dòng):nand flash驅(qū)動(dòng)看這一篇就夠了》。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問(wèn)。

一,前言

nand flash驅(qū)動(dòng)開(kāi)發(fā)總結(jié),涉及到s3c2440芯片nand flash控制器的設(shè)置及操作、K9F2G08U0C nand flash的設(shè)置及操作、平臺(tái)總線-驅(qū)動(dòng)-設(shè)備模型等相關(guān)知識(shí)。

二,硬件電路

2.1 Nand flash相關(guān)

LDATA0~LDATA7:傳輸命令、地址和數(shù)據(jù)。
RnB:nand flash的工作狀態(tài),0表示就緒,1表示正忙。
CLE:決定DATA0~DATA7傳輸?shù)氖菙?shù)據(jù)還是命令,1為命令,0為數(shù)據(jù)。
nFCE:nand flash的片選,1表示選中,0表示未選中(選中才能對(duì)其進(jìn)行操作)。
ALE:決定DATA0~DATA7傳輸?shù)氖菙?shù)據(jù)還是地址,1為地址,0為數(shù)據(jù)(CLE和ALE為0)。
nFWE:為0表示寫(xiě)操作(寫(xiě)命令、地址、數(shù)據(jù))。
nFRE:為0表示讀操作。
《Linux驅(qū)動(dòng):nand flash驅(qū)動(dòng)看這一篇就夠了》

2.2 S3c2440相關(guān)

根據(jù)Nand Flash的芯片書(shū)冊(cè)知,其需要五個(gè)字節(jié)表示地址,即五個(gè)地址周期,其一頁(yè)的大小為2KB,8位數(shù)據(jù)/地址傳輸。根據(jù)S3c2440芯片書(shū)冊(cè)可知,需要NCON、GPG13、GPG14為1,GPG15為0,即前者加上拉電阻,后者加下拉電阻。
《Linux驅(qū)動(dòng):nand flash驅(qū)動(dòng)看這一篇就夠了》
《Linux驅(qū)動(dòng):nand flash驅(qū)動(dòng)看這一篇就夠了》
《Linux驅(qū)動(dòng):nand flash驅(qū)動(dòng)看這一篇就夠了》

2.3 Nand flash 位反轉(zhuǎn)

由于Nand Flash的固有特性,在讀寫(xiě)數(shù)據(jù)過(guò)程中,偶然會(huì)產(chǎn)生一位或幾位數(shù)據(jù)錯(cuò)誤(這種概率很低),bit位從“1”變?yōu)椤?”,或者從“1”變?yōu)椤?”。當(dāng)位反轉(zhuǎn)發(fā)生在關(guān)鍵的代碼、數(shù)據(jù)上時(shí),有可能導(dǎo)致系統(tǒng)崩潰。當(dāng)僅僅是報(bào)告位反轉(zhuǎn),重新讀取即可。如果確實(shí)發(fā)生了位反轉(zhuǎn),則必須有相應(yīng)的錯(cuò)誤檢測(cè)/恢復(fù)措施。在NAND Flash上發(fā)生位反轉(zhuǎn)的概率很高,推薦使用EDC/ECC進(jìn)行錯(cuò)誤檢測(cè)和恢復(fù)。

三,Nand flash驅(qū)動(dòng)框架

《Linux驅(qū)動(dòng):nand flash驅(qū)動(dòng)看這一篇就夠了》

四,S3c2440 Nand Flash驅(qū)動(dòng)的加載過(guò)程

S3c2440 Nand Flash驅(qū)動(dòng)使用了平臺(tái)總線-驅(qū)動(dòng)-設(shè)備模型。

4.1 S3c2440 Nand Flash – 設(shè)備注冊(cè)

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

將上面的宏展開(kāi)

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"的類(lèi)型,放在 section(“.arch.info.init”),是初始化數(shù)據(jù),Kernel 起來(lái)之后將被丟棄。
各個(gè)成員函數(shù)在不同時(shí)期被調(diào)用:

  1. .init_machine 在 arch/arm/kernel/setup.c 中被 customize_machine 調(diào)用,放在 arch_initcall() 段里面,會(huì)自動(dòng)按順序被調(diào)用。
  2. init_irq在start_kernel() -> init_IRQ() -> init_arch_irq() 被調(diào)用
  3. map_io 在 setup_arch() -> paging_init() -> devicemaps_init()被調(diào)用
    其他主要都在 setup_arch() 中用到。

系統(tǒng)初始化時(shí),會(huì)調(diào)用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;
    ......
    // 注冊(cè)到平臺(tái)總線
	platform_add_devices(smdk_devs, ARRAY_SIZE(smdk_devs));
	......
}

// 配置了nand flash各信號(hào)脈沖寬度或時(shí)間間隔,以及分區(qū)
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,
};
// 設(shè)置nand flash的分區(qū)
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的具體分區(qū),四個(gè)分區(qū)
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設(shè)備注冊(cè)到平臺(tái)總線
// 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 – 驅(qū)動(dòng)注冊(cè)

make menuconfig,將s3c2440 nand flash驅(qū)動(dòng)加載到內(nèi)核中,系統(tǒng)啟動(dòng)時(shí)便會(huì)自動(dòng)加載驅(qū)動(dòng)。


-> Device Drivers                                                                                               │   
  │       -> Memory Technology Device (MTD) support (MTD [=y])                                                          │   
  │         -> NAND Device Support (MTD_NAND [=y])  
                  │ │     <*>   NAND Flash support for S3C2410/S3C2440 SoC                                            │ │   

加載驅(qū)動(dòng),調(diào)用驅(qū)動(dòng)初始化函數(shù)–s3c2410_nand_init
這里注冊(cè)了三個(gè)nand flash驅(qū)動(dòng)到平臺(tái)總線。會(huì)根據(jù)注冊(cè)到平臺(tái)總線的nand flash設(shè)備進(jìn)行匹配,選擇使用哪個(gè)驅(qū)動(dòng)。

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 – 設(shè)備-驅(qū)動(dòng)匹配

4.3.1 設(shè)備注冊(cè)到平臺(tái)總線時(shí)的匹配

platform_add_devices->
    platform_device_register->
        platform_device_add->
            device_add->
                bus_attach_device(dev)->
                    device_attach(dev)->
                        // 因?yàn)槭亲?cè)到平臺(tái)總線,
    					// 所以從平臺(tái)總線的驅(qū)動(dòng)鏈表中取出每一個(gè)驅(qū)動(dòng)和該設(shè)備進(jìn)行匹配
                        bus_for_each_drv(dev->bus, NULL, dev, __device_attach)->
                            __device_attach->
                                driver_probe_device(drv, dev)->	
                                    // 因?yàn)槭亲?cè)到平臺(tái)總線,調(diào)用平臺(tái)總線的匹配函數(shù),
    								// 即platform_match
                                    if (drv->bus->match && !drv->bus->match(dev, drv))

// 平臺(tái)總線的匹配函數(shù),通過(guò)比較驅(qū)動(dòng)和設(shè)備的名稱(chēng)進(jìn)行匹配                                       
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 驅(qū)動(dòng)注冊(cè)到平臺(tái)總線時(shí)的匹配

platform_driver_register->
    driver_register->
        bus_add_driver->
            driver_attach->
                // 因?yàn)槭亲?cè)到平臺(tái)總線,
    			// 所以從平臺(tái)總線的設(shè)備鏈表中取出每一個(gè)設(shè)備和該驅(qū)動(dòng)進(jìn)行匹配
                bus_for_each_dev(drv->bus, NULL, drv, __driver_attach)->
                    __driver_attach->
                        driver_probe_device->
                            // 因?yàn)槭亲?cè)到平臺(tái)總線,調(diào)用平臺(tái)總線的匹配函數(shù),
                            // 即platform_match
                            if (drv->bus->match && !drv->bus->match(dev, drv))

// 平臺(tái)總線的匹配函數(shù),通過(guò)比較驅(qū)動(dòng)和設(shè)備的名稱(chēng)進(jìn)行匹配                                       
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 – 調(diào)用驅(qū)動(dòng)的probe函數(shù)

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)) 匹配成功后調(diào)用really_probe函數(shù)
really_probe->
    // 因?yàn)樽?cè)的是平臺(tái)總線,所以看平臺(tái)總線是否具有probe函數(shù)
    // 如果平臺(tái)總線具有probe函數(shù)則調(diào)用平臺(tái)總線的probe函數(shù),否則直接調(diào)用驅(qū)動(dòng)的probe函數(shù)
    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;
	}
// 平臺(tái)總線具有probe函數(shù)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);
	// 調(diào)用驅(qū)動(dòng)的probe函數(shù)
	return drv->probe(dev);
}

// 設(shè)備
struct platform_device s3c_device_nand = {
	.name		  = "s3c2410-nand",
	.id		  = -1,
	.num_resources	  = ARRAY_SIZE(s3c_nand_resource),
	.resource	  = s3c_nand_resource,
};
// 驅(qū)動(dòng)
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,
	},
};

// 通過(guò)名稱(chēng)兩者匹配,即調(diào)用s3c2410_nand_probe

五,驅(qū)動(dòng)的probe函數(shù)分析

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

5.1 使能時(shí)鐘

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驅(qū)動(dòng):nand flash驅(qū)動(dòng)看這一篇就夠了》

#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控制器設(shè)置

5.3.1 設(shè)置脈沖寬度和間隔

脈沖寬度和信號(hào)間隔

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 設(shè)置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讀寫(xiě)數(shù)據(jù)接口
	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控制器數(shù)據(jù)寫(xiě)入的寄存器地址
		chip->IO_ADDR_W = regs + S3C2410_NFDATA;
		info->sel_reg   = regs + S3C2410_NFCONF;
		info->sel_bit	= S3C2410_NFCONF_nFCE;
		// 提供nand flash 寫(xiě)命令或者地址接口
		chip->cmd_ctrl  = s3c2410_nand_hwcontrol;
		// 提供nand flash 判斷就緒或忙狀態(tài)接口
		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控制器數(shù)據(jù)讀取的寄存器地址
	chip->IO_ADDR_R = chip->IO_ADDR_W;

	nmtd->info	   = info;
	nmtd->mtd.priv	   = chip;
	nmtd->mtd.owner    = THIS_MODULE;
    // 設(shè)置分區(qū)信息
	nmtd->set	   = set;
	// 使用硬件ecc校驗(yàn)還是軟件ecc校驗(yàn),以解決位反轉(zhuǎn)問(wèn)題。這里使用軟件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) ->
        // 設(shè)置默認(rèn)的nand flash接口,
    	// 比如設(shè)置struct nand_chip時(shí)提供的nand flash讀寫(xiě)數(shù)據(jù)接口等
        // 像5.4小節(jié)事先提供了則使用提供的,否則使用默認(rèn)的
        nand_set_defaults(chip, busw);
        // 從nand flash中讀出其設(shè)備id ,和nand_flash_ids數(shù)組中定義的各型號(hào)flash信息對(duì)比,
    	// 得到其flash型號(hào)。比如讀到K9F2G08U0C nand flash的設(shè)備Id為da,
        // 則從nand_flash_ids數(shù)組中可得其信息為
		// {"NAND 256MiB 3,3V 8-bit",	0xDA, 0, 256, 0, LP_OPTIONS},
        nand_get_flash_type(mtd, chip, busw, &nand_maf_id);
	// 填充所有未初始化的函數(shù)指針(比如nand_erase等接口),并在適當(dāng)?shù)那闆r下掃描壞的塊表。
	nand_scan_tail(mtd);

5.6 添加分區(qū)

5.6.1 遍歷mtd_notifiers,通過(guò)其add接口添加分區(qū)

s3c2410_nand_add_partition ->
    // 在s3c2410_nand_init_chip函數(shù)中已經(jīng)將分區(qū)信息設(shè)置給了mtd 
    add_mtd_device(&mtd->mtd) ->
        // 遍歷mtd_notifiers,通過(guò)其add接口添加分區(qū)
        list_for_each(this, &mtd_notifiers) {
				struct mtd_notifier *not = list_entry(this, struct mtd_notifier, list);
				not->add(mtd);
			}

5.6.2 設(shè)置mtd_notifiers鏈表

5.6.2.1 nand flash 字符設(shè)備
// 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;
	// 創(chuàng)建字符設(shè)備 設(shè)備節(jié)點(diǎn)為/dev/mtd%d
	class_device_create(mtd_class, NULL, MKDEV(MTD_CHAR_MAJOR, mtd->index*2),
			    NULL, "mtd%d", mtd->index);
	// 創(chuàng)建字符設(shè)備 設(shè)備節(jié)點(diǎn)為/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的塊設(shè)備
// 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 通過(guò)其add_mtd函數(shù)添加分區(qū)
    list_for_each(this, &blktrans_majors) {
		struct mtd_blktrans_ops *tr = list_entry(this, struct mtd_blktrans_ops, list);
		tr->add_mtd(tr, mtd);
	}


// 設(shè)置blktrans_majors鏈表 兩個(gè)地方設(shè)置,一個(gè)mtdblock_ro.c 一個(gè)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 塊設(shè)備初始化請(qǐng)求隊(duì)列

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

六,總結(jié)

6.1 nand flash字符設(shè)備創(chuàng)建過(guò)程

系統(tǒng)啟動(dòng),內(nèi)核初始化時(shí)加載mtdchar模塊,調(diào)用init_mtdchar,將 struct mtd_notifier 結(jié)構(gòu)注冊(cè)到mtd_notifiers鏈表中,供后續(xù)nand flash驅(qū)動(dòng)程序使用。

// 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;
	// 創(chuàng)建字符設(shè)備 設(shè)備節(jié)點(diǎn)為/dev/mtd%d
	class_device_create(mtd_class, NULL, MKDEV(MTD_CHAR_MAJOR, mtd->index*2),
			    NULL, "mtd%d", mtd->index);
	// 創(chuàng)建字符設(shè)備 設(shè)備節(jié)點(diǎn)為/dev/mtd%dro (只讀)
	class_device_create(mtd_class, NULL,
			    MKDEV(MTD_CHAR_MAJOR, mtd->index*2+1),
			    NULL, "mtd%dro", mtd->index);
}

nand flash驅(qū)動(dòng)程序,調(diào)用s3c2410_nand_add_partition接口添加分區(qū)時(shí),調(diào)用struct mtd_notifier結(jié)構(gòu)中的add函數(shù)添加分區(qū),創(chuàng)建字符設(shè)備節(jié)點(diǎn)。

s3c2410_nand_add_partition ->
    // 在s3c2410_nand_init_chip函數(shù)中已經(jīng)將分區(qū)信息設(shè)置給了mtd 
    add_mtd_device(&mtd->mtd) ->
        // 遍歷mtd_notifiers,通過(guò)其add接口添加分區(qū)
        list_for_each(this, &mtd_notifiers) {
				struct mtd_notifier *not = list_entry(this, struct mtd_notifier, list);
				not->add(mtd);
			}

6.2 nand flash塊設(shè)備創(chuàng)建過(guò)程

系統(tǒng)啟動(dòng),內(nèi)核初始化時(shí)加載mtdblock模塊,調(diào)用init_mtdblock,將struct mtd_notifier 結(jié)構(gòu)注冊(cè)到mtd_notifiers鏈表中,注冊(cè)塊設(shè)備,初始化請(qǐng)求隊(duì)列,注冊(cè)struct mtd_blktrans_ops結(jié)構(gòu)到blktrans_majors鏈表,供后續(xù)nand flash驅(qū)動(dòng)程序使用。(mtdblock_ro.c 和 mtdblock.c類(lèi)似操作)

init_mtdblock -> 
    register_mtd_blktrans(&mtdblock_tr) ->
        // 將struct mtd_notifier 結(jié)構(gòu)注冊(cè)到mtd_notifiers鏈表中
        if (!blktrans_notifier.list.next)
			register_mtd_user(&blktrans_notifier);
    	// 注冊(cè)塊設(shè)備 /dev/mtdblock
    	ret = register_blkdev(tr->major, tr->name);
        // 初始化請(qǐng)求隊(duì)列
    	tr->blkcore_priv->rq = blk_init_queue(mtd_blktrans_request, &tr->blkcore_priv->queue_lock);
        // 注冊(cè)struct mtd_blktrans_ops結(jié)構(gòu)到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驅(qū)動(dòng)程序,調(diào)用s3c2410_nand_add_partition接口添加分區(qū)時(shí),調(diào)用struct mtd_notifier結(jié)構(gòu)中的add函數(shù)添加分區(qū),調(diào)用struct mtd_notifier結(jié)構(gòu)中的add函數(shù)(blktrans_notify_add),即調(diào)用struct mtd_blktrans_ops結(jié)構(gòu)中的add_mtd函數(shù)(mtdblock_add_mtd)。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-410029.html

s3c2410_nand_add_partition ->
    // 在s3c2410_nand_init_chip函數(shù)中已經(jīng)將分區(qū)信息設(shè)置給了mtd 
    add_mtd_device(&mtd->mtd) ->
        // 遍歷mtd_notifiers,通過(guò)其add接口添加分區(qū)
        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 一次應(yīng)用層讀取Nand Flash數(shù)據(jù)的過(guò)程

// 對(duì)塊設(shè)備節(jié)點(diǎn)/dev/mtdblock進(jìn)行訪問(wèn)操作

//一個(gè)讀數(shù)據(jù)的bio,被合并或被生成一個(gè)請(qǐng)求,觸發(fā)請(qǐng)求隊(duì)列的請(qǐng)求處理函數(shù)
mtd_blktrans_request ->
    // 喚醒一個(gè)休眠線程--mtd_blktrans_thread,該線程在register_mtd_blktrans中啟動(dòng)
	wake_up_process(tr->blkcore_priv->thread);

// 該線程從請(qǐng)求隊(duì)列中取出一個(gè)請(qǐng)求調(diào)用do_blktrans_request接口進(jìn)行處理
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接口中被設(shè)置 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
    							// 在驅(qū)動(dòng)程序中 s3c2410_nand_init_chip函數(shù)里設(shè)置
    							// 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寄存器中讀取數(shù)據(jù)
                                    // chip->IO_ADDR_W = regs + S3C2410_NFDATA;
                                    // chip->IO_ADDR_R = chip->IO_ADDR_W;
                                    readsb(this->IO_ADDR_R, buf, len);                

到了這里,關(guān)于《Linux驅(qū)動(dòng):nand flash驅(qū)動(dòng)看這一篇就夠了》的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來(lái)自互聯(lián)網(wǎng)用戶(hù)投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • 外網(wǎng)SSH遠(yuǎn)程連接linux服務(wù)器,看這一篇就夠了

    外網(wǎng)SSH遠(yuǎn)程連接linux服務(wù)器,看這一篇就夠了

    轉(zhuǎn)載自?xún)?nèi)網(wǎng)穿透工具的文章:無(wú)公網(wǎng)IP,SSH遠(yuǎn)程連接Linux CentOS服務(wù)器【內(nèi)網(wǎng)穿透】 本次教程我們來(lái)實(shí)現(xiàn)如何在外公網(wǎng)環(huán)境下,SSH遠(yuǎn)程連接家里/公司的Linux CentOS服務(wù)器,無(wú)需公網(wǎng)IP,也不需要設(shè)置路由器。 公網(wǎng)SSH遠(yuǎn)程Linux CentOS服務(wù)器【內(nèi)網(wǎng)穿透】 cpolar官網(wǎng):https://www.cpolar.com

    2023年04月27日
    瀏覽(26)
  • 還不會(huì)在Linux上面如何部署jar包?看這一篇就夠了

    還不會(huì)在Linux上面如何部署jar包?看這一篇就夠了

    1、用WINSCP工具把對(duì)應(yīng)的jar包上傳到linux服務(wù)器指定位置 2、利用putty工具連接服務(wù)器,輸入 cd / 命令切換到根目錄; cd home/ 切換到j(luò)ar包目錄,我是放在了home目錄下,這個(gè)要切換到對(duì)應(yīng)的目錄。 ll 命令查看目錄下文件 3、輸入命令啟動(dòng)jar包 一、后臺(tái)啟動(dòng),關(guān)閉當(dāng)前窗口程序繼續(xù)

    2024年02月12日
    瀏覽(18)
  • 1秒將本地SpringBoot項(xiàng)目jar包部署到Linux環(huán)境(看這一篇就夠了)

    1秒將本地SpringBoot項(xiàng)目jar包部署到Linux環(huán)境(看這一篇就夠了)

    在IDEA編輯器中 一鍵 將本地已經(jīng)打包好的 SpringBoot 項(xiàng)目部署到 linux 環(huán)境,實(shí)現(xiàn) 秒級(jí) 部署 前置知識(shí):需掌握Maven的打包 在windows環(huán)境使用命令啟動(dòng)SpringBoot項(xiàng)目 在Linux環(huán)境使用命令啟動(dòng)項(xiàng)目 在Linux環(huán)境使用腳本啟動(dòng)項(xiàng)目 優(yōu)化腳本,每次啟動(dòng)時(shí)先關(guān)閉舊項(xiàng)目 分離依賴(lài),每次上傳

    2024年02月04日
    瀏覽(89)
  • CSS基礎(chǔ)——看這一篇就夠了

    CSS基礎(chǔ)——看這一篇就夠了

    目錄 一、CSS簡(jiǎn)介 1.CSS是什么? 2.CSS的作用 3.CSS的構(gòu)成 二、CSS選擇器 1.基礎(chǔ)選擇器 (1).標(biāo)簽選擇器 (2)類(lèi)選擇器 (3)標(biāo)簽選擇器 (4) 通配符選擇器 2.復(fù)合選擇器 (1)后代選擇器(包含選擇器) (2)子選擇器 (3)并集選擇器 (4)偽類(lèi)選擇器 ?三、基本屬性 1.字體屬性

    2024年02月09日
    瀏覽(98)
  • 精通線程池,看這一篇就夠了

    精通線程池,看這一篇就夠了

    當(dāng)我們運(yùn)用多線程技術(shù)處理任務(wù)時(shí),需要不斷通過(guò)new的方式創(chuàng)建線程,這樣頻繁創(chuàng)建和銷(xiāo)毀線程,會(huì)造成cpu消耗過(guò)多。那么有沒(méi)有什么辦法 避免頻繁創(chuàng)建線程 呢? 當(dāng)然有,和我們以前學(xué)習(xí)過(guò)多連接池技術(shù)類(lèi)似,線程池通過(guò)提前創(chuàng)建好線程保存在線程池中, 在任務(wù)要執(zhí)行時(shí)取

    2023年04月17日
    瀏覽(97)
  • ElasticSearch常見(jiàn)用法,看這一篇就夠了

    ElasticSearch常見(jiàn)用法,看這一篇就夠了

    2024送書(shū)福利正式起航 關(guān)注「哪吒編程」,提升Java技能 文末送3本《一本書(shū)講透Elasticsearch:原理、進(jìn)階與工程實(shí)踐》 大家好,我是哪吒。 ElasticSearch是一款由Java開(kāi)發(fā)的開(kāi)源搜索引擎,它以其出色的實(shí)時(shí)搜索、穩(wěn)定可靠、快速安裝和方便使用的特性,在Java開(kāi)發(fā)社區(qū)中贏得了廣

    2024年03月19日
    瀏覽(94)
  • 超圖(HyperGraph)學(xué)習(xí),看這一篇就夠了

    超圖(HyperGraph)學(xué)習(xí),看這一篇就夠了

    最近事多,好久沒(méi)更新了,隨便寫(xiě)寫(xiě)(Ctrl+V)點(diǎn) 一、超圖定義 通常圖論中的圖,一條edge只能連接2個(gè)vertex,在超圖中,不限量 如何理解呢,就用我正在做的KT問(wèn)題來(lái)看:7道題目-7個(gè)頂點(diǎn);4種概念-4條超邊,其中第1,2,3題都是考察概念1的,則構(gòu)建一個(gè)包含了這仨的超邊,以此類(lèi)

    2024年02月02日
    瀏覽(27)
  • SourceTree使用看這一篇就夠了

    SourceTree使用看這一篇就夠了

    ?你夢(mèng)想有一天成為git大師,然而面對(duì)復(fù)雜的git命令,你感覺(jué)TMD這我能記得住嗎?你曾經(jīng)羨慕從命令行敲git命令,才會(huì)更加炫酷,然而時(shí)間一長(zhǎng),TMD命令我有忘了。那么今天我介紹的這款工具會(huì)讓你從git命令中解救出來(lái),這就是git可視化工具SourcTree。 事實(shí)上Git的功能十分強(qiáng)大

    2024年02月08日
    瀏覽(23)
  • Docker Volume 看這一篇就夠了

    Docker Volume 看這一篇就夠了

    默認(rèn)情況下,在容器內(nèi)創(chuàng)建的所有文件都存儲(chǔ)在可寫(xiě)容器層上。這意味著: 當(dāng)該容器不再存在時(shí),數(shù)據(jù)不會(huì)持續(xù)存在,并且如果另一個(gè)進(jìn)程需要數(shù)據(jù),則可能很難將數(shù)據(jù)從容器中取出。 容器的可寫(xiě)層與運(yùn)行容器的主機(jī)緊密耦合。您無(wú)法輕松地將數(shù)據(jù)移動(dòng)到其他地方。 寫(xiě)入容

    2024年02月02日
    瀏覽(106)
  • CAS自旋鎖,看這一篇就夠了

    前序 時(shí)隔多年,杰倫終于出了新專(zhuān)輯,《最偉大的作品》讓我們穿越到1920年,見(jiàn)到了馬格利特的綠蘋(píng)果、大利的超現(xiàn)實(shí)、常玉畫(huà)的大腿、莫奈的睡蓮、徐志摩的詩(shī)… 他說(shuō)“最偉大的作品”并不是自己的歌,而是這個(gè)世界上最偉大的藝術(shù)作品們。 為什么要寫(xiě)CAS自旋鎖呢?最近

    2023年04月08日
    瀏覽(19)

覺(jué)得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包