一,前言
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表示讀操作。
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,即前者加上拉電阻,后者加下拉電阻。
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)框架
四,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)用:
- .init_machine 在 arch/arm/kernel/setup.c 中被 customize_machine 調(diào)用,放在 arch_initcall() 段里面,會(huì)自動(dòng)按順序被調(diào)用。
- init_irq在start_kernel() -> init_IRQ() -> init_arch_irq() 被調(diào)用
- 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 端口映射
#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(¬ifier) ->
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(¬ifier) ->
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)似操作)文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-410029.html
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)!