`
womendu
  • 浏览: 1516680 次
  • 性别: Icon_minigender_2
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

MTD系列 - linux内核底层nand驱动解析

阅读更多

前言:
前几篇文章基本都没有涉及到mtd原始设备层以下的内容,其实mtd块设备层和mtd原始设备层的分界线很明显,只是通过
mtd_table[]和mtd_notifiers链表来联系,具体怎么联系的请参考上一篇文章。
本文中将详细分析linux内核中nand设备注册和驱动注册,同时文中会穿插关于nand坏块管理的部分,另外在适当的地方会
讲解DMA原理和其在nand驱动程序中的应用。

* OS : linux2.6.29
* SOC : pxa935
* NAND : Hynix(512MB 1.8V 16-bit) - H8BCS0UN0MCR
* Author: 李枝果/lizgo 2010-11-9 lizhiguo0532@163.com
* note : 本文中涉及的内容基本都是平台相关的,读者可只关注共性的东西


2.6版本的linux内核驱动模型中流传着一个时髦的词:“platform”,其中也存在platform device和platform driver,
内核中使用platform bus统一管理这些设备和驱动,所以注册包括device和driver的注册。
一、platform device注册
MACHINE_START和MACHINE_END定义的结构体是平台相关的,这里定义如下:
MACHINE_START(BENZGLB, "Benzglb")
.phys_io = 0x40000000,
.boot_params = 0xa0000100,
.io_pg_offst = (io_p2v(0x40000000) >> 18) & 0xfffc,

.map_io = pxa_map_io,
// start_kernel()-->setup_arch()-->paging_init()-->devicemaps_init()-->pxa_map_io
.init_irq = pxa3xx_init_irq,
// 在setup_arch()中被赋值给全局函数指针init_arch_irq
// start_kernel()-->init_IRQ()-->"init_arch_irq()"间接调用pxa3xx_init_irq
.timer = &pxa_timer,
// 在setup_arch()中被赋值给全局struct sys_timer对象指针system_timer
// start_kernel()-->time_init-->"system_timer->init()"间接调用pxa_timer.init = pxa_timer_init()
.init_machine = benzglb_init,
// 在setup_arch()中被赋值给全局函数指针init_machine
// 由于arch_initcall(customize_machine),所以在start_kernel()-->rest_init()
// -->kernel_init()-->do_basic_setup()-->do_initcalls()的第3个等级上被调用
// init.h
MACHINE_END
/*******************************
其中的宏定义于文件:arch/arm/include/asm/mach/arch.h
#define MACHINE_START(_type,_name) \
static const struct machine_desc __mach_desc_##_type \
__used \
__attribute__((__section__(".arch.info.init"))) = { \
.nr = MACH_TYPE_##_type, \
.name = _name,

#define MACHINE_END \
};
将上面的展开,实际上就是定义了一个结构体:
static const struct machine_desc __mach_desc_BENZGLB __used \
__attribute__((__section__(".arch.info.init"))) = {
.nr = MACH_TYPE_BENZGLB,
.name = "Benzglb",
.phys_io = 0x40000000,
.boot_params = 0xa0000100,
.io_pg_offst = (io_p2v(0x40000000) >> 18) & 0xfffc,
.map_io = pxa_map_io,
.init_irq = pxa3xx_init_irq,
.timer = &pxa_timer,
.init_machine = benzglb_init,
};
*******************************/
benzglb_init()函数可谓是重量级的了,初始化了很多东西。但是是在哪里调用该函数的呢?或许从上面的注释你也可以
注意到了,下面就再来wlak一下:
start_kernel()
--> setup_arch()
--> ...
--> init_machine = mdesc->init_machine;
--> ...
init_machine是文件arch/arm/kernel/setup.c中的静态全局变量,定义和调用如下:
static void (*init_machine)(void) __initdata;
static int __init customize_machine(void)
{
/* customizes platform devices, or adds new ones */
if (init_machine)
init_machine();
return 0;
}
arch_initcall(customize_machine); // init.h initcall3
可以看到customize_machine()函数将会在do_initcalls()的第3个等级上被调用,接着就会调用函数init_machine(),
也就是函数benzglb_init():
do_initcalls()
--> customize_machine()
--> init_machine() == benzglb_init()
--> benzina_init_nand() // 该函数中我们只关注nand初始化
看来有必要将benzina_init_nand()全部列出来看一看了:
static struct pxa3xx_nand_platform_data benzina_nand_info;
/******
struct pxa3xx_nand_platform_data {
struct mtd_partition *parts;
unsigned int nr_parts;
};
******/
static void __init benzina_init_nand(void)
{
benzina_nand_info.parts = android_256m_v75_partitions; // nand分区数组
benzina_nand_info.nr_parts = ARRAY_SIZE(android_256m_v75_partitions); // nand分区数目

pxa3xx_device_nand.dev.platform_data = &benzina_nand_info;
platform_device_register(&pxa3xx_device_nand);
}
android_256m_v75_partitions定义于arch/arm/mach-pxa/include/mach/part_table.h中,这是一个
struct mtd_partition类型结构体的数组,描述了系统上nand分区情况:name、offset、size等。
pxa3xx_device_nand结构体对象是struct platform_device类型:
static u64 pxa3xx_nand_dma_mask = DMA_BIT_MASK(32);
static struct resource pxa3xx_resource_nand[] = {
[0] = {
.start = 0x43100000,
.end = 0x431000ff,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = IRQ_NAND,
.end = IRQ_NAND,
.flags = IORESOURCE_IRQ,
},
};

struct platform_device pxa3xx_device_nand = {
.name = "pxa3xx-nand",
.id = -1,
.dev = {
.dma_mask = &pxa3xx_nand_dma_mask,
.coherent_dma_mask = DMA_BIT_MASK(32),
},
.resource = pxa3xx_resource_nand, // see up
.num_resources = ARRAY_SIZE(pxa3xx_resource_nand),
};
struct platform_device结构体的定义位于文件include/linux/platform_device.h中:
struct platform_device {
const char * name;
int id;
struct device dev;
u32 num_resources;
struct resource * resource;
};
benzina_init_nand()函数中将描述nand分区的结构体benzina_nand_info与描述nand device的结构体
联系起来:
pxa3xx_device_nand.dev.platform_data = &benzina_nand_info;
最后调用函数platform_device_register(&pxa3xx_device_nand)将nand的平台设备注册进系统的设备树内。
对于注册的过程这里就不跟踪了,如果有兴趣,可参考我的另篇文章或自行分析。
二、platform driver注册
该部分的内容位于文件drivers/mtd/nand/pxa3xx_nand.c
static struct platform_driver pxa3xx_nand_driver = {
.driver = {
.name = "pxa3xx-nand",
},
.probe = pxa3xx_nand_probe,
.remove = pxa3xx_nand_remove,
#ifdef CONFIG_PM // 电源管理的部分
.suspend = pxa3xx_nand_suspend,
.resume = pxa3xx_nand_resume,
#endif
};
static int __init pxa3xx_nand_init(void)
{
...
return platform_driver_register(&pxa3xx_nand_driver);
}
module_init(pxa3xx_nand_init);
这里说明一点:device和driver的注册其实是没有先后之分的,device注册的时候除了将自己挂在platform bus上外,另外
会去遍历该bus上的所有drivers,直到匹配到(device和driver的名字相同)一个driver为止。而driver注册的时候,也是除
了将自己挂在platfrom bus上之外,另外也会去遍历该bus上的所有设备区匹配,这里和前面不同的是,它会去找到多有该
driver可以管理到的设备为止。下面就跟踪一下driver注册时,如何调用到probe函数的,又如何传递了
struct platform_device的参数:
platform_driver_register(&pxa3xx_nand_driver)
--> drv->driver.bus = &platform_bus_type
--> drv->driver.probe = platform_drv_probe
--> ...
--> driver_register(&drv->driver)
--> driver_find(drv->name, drv->bus) // 线检查是否已经注册过了
--> bus_add_driver(drv)
--> driver_attach(drv)
--> bus_for_each_dev(drv->bus, NULL, drv, __driver_attach)
--> fn(dev, data) = __driver_attach(dev, data) // dev - each device on platfrom bus
// data - driver
--> driver_probe_device(drv, dev)
--> drv->bus->match(dev, drv) = platform_match(dev, drv) // 名字匹配
--> really_probe(dev, drv)
--> drv->probe(dev) = platform_drv_probe(dev)
--> 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);

return drv->probe(dev);
}
-->pxa3xx_nand_probe(&pxa3xx_device_nand);
...
到这个过程中,我们就还可以看到,driver注册的时候,会优先使用platform bus
的probe函数,如果它的probe函数为NULL,那么就使用注册driver的probe函数
(前提是要存在probe函数)
三、pxa3xx_nand_probe()函数分析
由于内容加多,参见文档:pxa3xx_nand_probe.c
四、底层几个关键结构体的联系
static struct mtd_info *monahans_mtd = NULL;
struct nand_chip *this;
struct pxa3xx_nand_info *info;
struct dfc_context dfc_context =
{
.dfc_mode = &dfc_mode,
};
static struct dfc_flash_info hynix4GbX16 =
{
.timing = {
.tCH = 10, /* tCH, Enable signal hold time */
.tCS = 35, /* tCS, Enable signal setup time */
.tWH = 15, /* tWH, ND_nWE high duration */
.tWP = 25, /* tWP, ND_nWE pulse time */
.tRH = 15, /* tRH, ND_nRE high duration */
.tRP = 25, /* tRP, ND_nRE pulse width */
/* tR = tR+tRR+tWB+1, ND_nWE high to ND_nRE low for read */
.tR = 25000,
/* tWHR, ND_nWE high to ND_nRE low delay for status read */
.tWHR = 60,
.tAR = 10, /* tAR, ND_ALE low to ND_nRE low delay */
},
.enable_arbiter = 1, /* Data flash bus arbiter enable */
.page_per_block = 64, /* Pages per block */
.row_addr_start = 1, /* third cycle start, Row address start position */
.read_id_bytes = 4, /* Returned ID bytes */
.dfc_mode = 0, /* NAND mode */
.ncsx = 0,
.page_size = 2048, /* Page size in bytes */
.oob_size = 64, /* OOB size in bytes */
.flash_width = 16, /* Width of Flash memory */
.dfc_width = 16, /* Width of flash controller */
.num_blocks = 4096, /* Number of physical blocks in Flash */ //modified sunqidong
.chip_id = 0xbcad, //modified sunqidong
.read_prog_cycles = 5, /* Read, Program Cycles */
/* command codes */
.read1 = 0x3000, /* Read */
.read2 = 0x0050, /* Read1 unused, current DFC don't support */
.program = 0x1080, /* Write, two cycle command */ //modified sunqidong
.read_status = 0x0070, /* Read status */
.read_id = 0x0090, /* Read ID */
.erase = 0xD060, /* Erase, two cycle command */
.reset = 0x00FF, /* Reset */
.lock = 0x002A, /* Lock whole flash */
.unlock = 0x2423, /* Unlock, two cycle command, supporting partial unlock */
.lock_status = 0x007A, /* Read block lock status */
.addr2ndcb1 = HYNIX4GbX16Addr2NDCB1,
.ndbbr2addr = HYNIX4GbX16NDBBR2Addr,
};
///////////////////////// 这几个底层关键结构体的联系 ////////////////////////////
context->flash_info = &hynix4GbX16
// 分配mtd_info、nand_chip、pxa3xx_nand_info的空间
monahans_mtd = kzalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip) +
sizeof(struct pxa3xx_nand_info) , GFP_KERNEL);
...
this = (struct nand_chip *)((void *)monahans_mtd + sizeof(struct mtd_info));
info = (struct pxa3xx_nand_info *)((void *)this + sizeof(struct nand_chip));
...
monahans_mtd->priv = this;
this->priv = info;
...
info->context = &dfc_context
...

分享到:
评论

相关推荐

    基于MTD的2440-大页nand驱动

    本文档由guolele撰写,主要讲解基于Linux内核版本2.6.26的MTD(Memory Technology Devices)大页NAND驱动的编写方法。该文档不仅适合对NAND闪存有一定了解的开发者,同时也为初学者提供了深入浅出的入门指导。NAND...

    mtd-linux.rar_LINUX MTD-UTILITY_linux mtd_linux 驱动 AT91RM9200

    这个压缩包"mtd-linux.rar"包含了在S3C2410和AT91RM9200微处理器上移植和调试过的Linux MTD驱动源码。 S3C2410是一款由Samsung制造的ARM920T内核的嵌入式处理器,广泛应用于嵌入式系统和移动设备。它内置了NAND闪存...

    mtd-for-linux.zip_mtd _mtd nand

    在你提供的压缩包"mtd-for-linux.zip_mtd_mtd_nand"中,包含了一系列与MTD相关的源代码文件,主要针对S3C2410芯片上的NAND Flash驱动。 S3C2410是一款由Samsung生产的微处理器,广泛应用于嵌入式系统和移动设备,它...

    mtd-utils安装包

    MTD是Linux内核中处理非易失性存储器(如EEPROM、EPROM、NOR和NAND闪存)的子系统。mtd-utils提供了诸如擦除、读写和检查MTD设备等功能,对于开发和维护嵌入式系统非常关键。 **mtd-utils-1.5.0.tar.bz2** 这是mtd-...

    mtd-utils-交叉编译与使用2正解

    `mtd-utils` 是一组用于管理内存技术驱动(Memory Technology Driver)的实用程序,它包含了诸如 `flash_eraseall`、`mknod` 和 `mkfs.jffs2` 等命令。本文主要介绍如何在 Ubuntu 10.04 上,使用 `arm-none-linux-...

    Linux MTD 下获取Nand flash 各个参数的过程的详细解析

    ### Linux MTD 下获取Nand flash 各个参数的过程的详细解析 #### 一、引言 在嵌入式系统开发中,NAND Flash 是一种非常重要的存储介质,广泛应用于移动设备、数字相机等产品中。Linux MTD (Memory Technology ...

    mtd-utils-2.0.2.tar

    mtd-utils-2.0.2.tar.bz2 在使用openwrt进行编译的时候很容易出现下载 mtd-utils-2.0.2.tar.bz2 失败,那么就将该资源下载后放到openwrt/dl/文件夹下面

    Linux_SPI-NAND_开发指南1

    - **MTD (Memory Technology Device)**:Linux内核中的一个子系统,负责抽象化不同类型的非易失性存储设备,如NAND、NOR等。 - **UBI (Unsorted Block Images)**:用于构建可靠文件系统的层,它管理物理ECC校验和...

    linux内核驱动详解1

    | | `-- mtd-utils-1.0.0.tar.gz | |-- nand驱动范例 | | `-- s3c2410.c | |-- nor驱动范例 | | `-- s3c2410nor.c | `-- yaffs&yaffs2;源代码 | |-- yaffs.tar.gz | `-- yaffs2.tar.gz |-- 20 | |-- USB串口驱动 | |...

    嵌入式Linux中的Nand Flash驱动详解.pdf

    该文详细地解释了Nand Flash驱动的实现机制,从设备的注册到驱动加载对Nand Flash控制器的驱动进行深入分析,同时阐述了Linux内核中的相关机制,为Nand Flash等设备驱动的移植及编写提供指导。 Nand Flash是一种非...

    嵌入式linux下升级系统工具之mtd-utils-v120,mtd-utils-v130等等版本

    mtd-utils是一个用于管理内存技术设备(Memory Technology Device, MTD)的工具集合,主要用于对Flash存储设备的操作,例如NOR和NAND闪存。这些工具支持创建、擦除、读取和写入操作,是嵌入式系统中更新固件和系统...

    Linux MTD下获取Nand flash各个参数的过程的详细解析

    # Linux MTD 下获取 NAND Flash 各个参数的过程的详细解析 ## 1. 引言 在嵌入式系统中,NAND Flash 是一种常用的存储设备,它被广泛应用于移动设备、数字相机以及各种小型电子设备中。Linux 的 Memory Technology ...

    linux在TQ2440上移植2--Nandflash驱动,MTD分区

    Linux内核2.6.32.2版本已经包含了大部分NAND Flash驱动程序,在`linux-2.6.35.3/drivers/mtd/nand/nand_ids.c`文件中定义了各种支持的NAND Flash类型。但这些默认配置并不一定适用于所有场景,特别是对于TQ2440这样...

    mtd-utils-1.4.8.tar.gz

    《mtd-utils-1.4.8源代码详解》 mtd-utils-1.4.8.tar.gz 是一个包含mtd-utils源代码的压缩包,主要用于处理和...无论是对驱动开发人员还是系统管理员,熟悉和掌握mtd-utils都能提升他们在嵌入式Linux领域的专业能力。

    mtd-nand-omap2.rar_ECC NAND

    文件“mtd-nand-omap2.c”可能包含了针对OMAP2(德州仪器的OMAP2处理器系列)平台实现的MTD NAND驱动程序代码。OMAP2是面向嵌入式应用的微处理器,它集成了NAND控制器,使得开发者可以直接处理NAND闪存的读写操作。...

    mtd-utils-arm

    mtd-utils是一系列命令行工具的集合,它们提供了一系列操作MTD设备的功能,包括分区管理、数据擦除、读写操作等。这些工具使得系统管理员和开发者能够更方便地进行设备的初始化、数据备份、故障排查等工作。其中包含...

    MTD.rar_mtd nand_nand flash mtd_nand flash说明_底层

    MTD(Memory Technology Device)是Linux内核中的一个子系统,专门用于管理非易失性存储设备,如闪存(Flash Memory)。这个子系统抽象了底层硬件,为上层的文件系统和其他用户提供了统一的接口。在Linux系统中,MTD...

    mtd-utils-1.0.0.tar.gz

    2. **API调用**:mtd-utils依赖于Linux内核的MTD框架,通过调用MTD驱动程序提供的接口进行操作。理解这些API可以帮助我们更好地控制MTD设备。 3. **编译与安装**:通过执行`./configure`、`make`和`make install`,...

    mini2440的-linux内核移植.docx

    - **交叉编译器**:arm-linux-gcc-4.3.2,由友善之臂提供,遵循EABI标准。 #### 二、下载并解压内核源码 1. **下载内核源码**:访问官方网址`http://www.kernel.org/`,选择适合mini2440的内核版本。文档中提到...

Global site tag (gtag.js) - Google Analytics