一,前言
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
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 起来之后将被丢弃。
各个成员函数在不同时期被调用:
- .init_machine 在 arch/arm/kernel/setup.c 中被 customize_machine 调用,放在 arch_initcall() 段里面,会自动按顺序被调用。
- init_irq在start_kernel() -> init_IRQ() -> init_arch_irq() 被调用
- 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 端口映射
#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(¬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;
// 创建字符设备 设备节点为/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(¬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;
// 创建字符设备 设备节点为/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类似操作)文章来源:https://www.toymoban.com/news/detail-410029.html
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模板网!