Linux NAND FLASH驱动程序框架分析

<div id="content">
<p>1.Linux-MTD Subsystem </p>
<p>FLASH在嵌入式系统中是必不可少的,它是bootloader、linux内核和文件系统的最佳载体。在Linux内核中引入了MTD子系统为NOR FLASH和NAND FLASH设备提供统一的接口,从而使得FLASH驱动的设计大为简化。</p>
<p>在引入MTD后Linux系统中FLASH设备驱动可分为四层,如图:</p>
<img src="http://www.linuxidc.com/upload/2011_01/11011418276979.png" alt="Linux NAND FLASH驱动程序框架分析" align="middle"><table style="width: 97%;" border="0" align="center"><tbody><tr>
<td colspan="3"><br></td>
</tr></tbody></table>
<p>1. 硬件驱动层</p>
<p>FLASH硬件驱动层负责FLASH硬件设备的读、写、擦出,LINUX MTD设备的NOR FLASH驱动位于/driver/mtd/chips子目录下,NAND FLASH驱动则位于/driver/mtd/nand子目录下。</p>
<p>2. MTD原始设备层:MTD原始设备层由两部分构成,一部分是MTD原始设备的通用代码(mtdcore.c、mtdpart.c),另一部分是各个特定的FLASH的数据,例如分区。</p>
<p>3. MTD设备层:基于MTD原始设备,LINUX系统可以定义出MTD的块设备(主设备号31)www.linuxidc.com和字符设备(设备号90),构成设备层。MTD字符设备在mtdchar.c实现,MTD块设备在mtdblock.c实现。</p>
<p>4. 设备节点:通过mknod在/dev子目录下建立MTD字符设备节点(主设备号为90)和块设备节点(主设备号为31),用户通过访问此设备节点即可访问MTD字符设备和块设备。</p>
<p>也可通过下图理解:</p>
<img src="http://www.linuxidc.com/upload/2011_01/11011418273428.png" alt="Linux NAND FLASH驱动程序框架分析" align="middle"><img src="http://www.linuxidc.com/upload/2011_01/11011418272183.jpg" alt="Linux NAND FLASH驱动程序框架分析" align="middle"><p>从上图可以看出,MTD设备层与原始设备层打交道。通过分析源代码我们可以知道当上层要求对FLASH进行读写时,它会像设备层发出请求,设备层的
读写函数会调用原始设备层中的读写函数,即mtd_info结构体(mtd原始设备层中描述设备的专用结构体)中的读写函数,而mtd_info中的函数
会调用nand_chip(nand硬件驱动层中描述设备的结构体,其中包含了针对特定设备的基本参数和设备操作函数)中的读写函数。所以当我们写一个
flash硬件驱动程序时,有以下步骤:</p>
<p>1. 如果FLASH要分区,则定义mtd_partition数组,将FLASH分区信息记录其中。</p>
<p>2. 在模块加载时为每一个chip(主分区)分配mtd_info和nand_chip的内存,根据目标板nand
控制器的特殊情况初始化nand_chip中的实现对FLASH操作的成员函数,如hwcontrol()、dev_ready()、
read_byte()、write_byte()等。填充mtd_info,并将其priv成员指向nand_chip。</p>
<p>3. 以mtd_info为参数调用nand_scan()函数探测NAND FLASH的存在。nand_scan()函数会从FLASH芯片中读取其参数,填充相应nand_chip成员。</p>
<p>4. 如果要分区,则以mtd_info和mtd_partition为参数调用add_mtd_partions(),添加分区信息。在这个函数里面会为每一个分区(不包含主分区)分配一个mtd_info结构体,www.linuxidc.com填充,并注册。</p>
<div id="content">
<p>2.nand flash驱动程序实例分析</p>
<p>我们以2.6.26内核中s3c2410的nand flash驱动程序为例来分析一下这个过程,这里的flash驱动被写成了platform驱动的形式。我们下面分析其过程:</p>
<p>1. 注册nand flash设备</p>
<p>nand flash分区:</p>
<p>linux2.6.26.8/arch/arm/plat-s3c24xx/common-smdk.c:</p>
<table style="width: 97%;" border="0" align="center"><tbody><tr>
<td colspan="3"><br></td>
</tr></tbody></table>
<p>static struct mtd_partition smdk_default_nand_part[] = {</p>
<p>[0] = {</p>
<p>name: "bootloader",</p>
<p>size: 0x00100000,</p>
<p>offset: 0x0,</p>
<p>},</p>
<p>[1] = {</p>
<p>name: "kernel",</p>
<p>size: 0x00300000,</p>
<p>offset: 0x00100000,</p>
<p>},</p>
<p>[2] = {</p>
<p>name: "root",</p>
<p>size: 0x02800000,</p>
<p>offset: 0x00400000,</p>
<p>},</p>
<p>};</p>
<p>static struct s3c2410_nand_set smdk_nand_sets[] = { //该数组为chip集合,这里我们只有一片chip</p>
<p>[0] = {</p>
<p>.name = "NAND",</p>
<p>.nr_chips = 1,</p>
<p>.nr_partitions = ARRAY_SIZE(smdk_default_nand_part),</p>
<p>.partitions = smdk_default_nand_part,</p>
<p>},</p>
<p>};</p>
<p>static struct s3c2410_platform_nand smdk_nand_info = { //这里将许多数据作为platform_data传入包括chip数组</p>
<p>.tacls = 20,</p>
<p>.twrph0 = 60,</p>
<p>.twrph1 = 20,</p>
<p>.nr_sets = ARRAY_SIZE(smdk_nand_sets),</p>
<p>.sets = smdk_nand_sets,</p>
<p>};</p>
<p>nand控制器资源:</p>
<p>linux2.6.26.8/arch/arm/plat-s3c24xx/devs.c</p>
<p>static struct resource s3c_nand_resource[] = {</p>
<p>[0] = {</p>
<p>.start = S3C2410_PA_NAND,</p>
<p>.end = S3C2410_PA_NAND + S3C24XX_SZ_NAND - 1,</p>
<p>.flags = IORESOURCE_MEM,</p>
<p>}</p>
<p>};</p>
<p>struct platform_device s3c_device_nand = {</p>
<p>.name = "s3c2410-nand",</p>
<p>.id = -1,</p>
<p>.num_resources = ARRAY_SIZE(s3c_nand_resource),</p>
<p>.resource = s3c_nand_resource,</p>
<p>};</p>
<p>注册nand flash作为platform device:</p>
<p>linux2.6.26.8/arch/arm/plat-s3c24xx/common-smdk.c:</p>
<p>static struct platform_device __initdata *smdk_devs[] = {</p>
<p>&amp;s3c_device_nand,</p>
<p>…</p>
<p>};</p>
<p>void __init smdk_machine_init(void)</p>
<p>{</p>
<p>…</p>
<p>s3c_device_nand.dev.platform_data = &amp;smdk_nand_info; //注意这里的赋值,在nand flash驱动程序的probe函数里面利用了这里赋值的数据</p>
<p>platform_add_devices(smdk_devs, ARRAY_SIZE(smdk_devs));</p>
<p>s3c2410_pm_init();</p>
<p>}</p>
<div id="content">
<p>2. 注册nand flash driver<br>
linux/drivers/mtd/nand/s3c2410.c:</p>
<p>static struct platform_driver s3c2410_nand_driver = {</p>
<p>.probe = s3c2410_nand_probe,</p>
<p>.remove = s3c2410_nand_remove,</p>
<table style="width: 97%;" border="0" align="center"><tbody><tr>
<td colspan="3"><br></td>
</tr></tbody></table>
<p>.suspend = s3c24xx_nand_suspend,</p>
<p>.resume = s3c24xx_nand_resume,</p>
<p>.driver = {</p>
<p>.name = "s3c2410-nand",</p>
<p>.owner = THIS_MODULE,</p>
<p>},</p>
<p>};</p>
<p>static int __init s3c2410_nand_init(void)</p>
<p>{</p>
<p>printk("S3C24XX NAND Driver, (c) 2004 Simtec Electronics/n");</p>
<p></p>
<p>platform_driver_register(&amp;s3c2412_nand_driver);</p>
<p>platform_driver_register(&amp;s3c2440_nand_driver);</p>
<p>return platform_driver_register(&amp;s3c2410_nand_driver);</p>
<p>}</p>
<p>module_init(s3c2410_nand_init);</p>
<p>当platform_driver驱动被加载时或者是当platform_device被注册时,总线驱动程序</p>
<p>会查找与设备匹配的驱动程序,找到时设备驱动程序的probe函数会被调用,下面我们来分析一下在我们驱动程序中的probe函数:</p>
<p>static int s3c2410_nand_probe(struct platform_device *dev)</p>
<p>{</p>
<p>return s3c24xx_nand_probe(dev, TYPE_S3C2410);</p>
<p>}</p>
<p>static int s3c24xx_nand_probe(struct platform_device *pdev,</p>
<p> enum s3c_cpu_type cpu_type)</p>
<p>{</p>
<p>struct s3c2410_platform_nand *plat = to_nand_plat(pdev);</p>
<p>struct s3c2410_nand_info *info;</p>
<p>struct s3c2410_nand_mtd *nmtd;</p>
<p>struct s3c2410_nand_set *sets;</p>
<p>struct resource *res;</p>
<p>int err = 0;</p>
<p>int size;</p>
<p>int nr_sets;</p>
<p>int setno;</p>
<p></p>
<p>pr_debug("s3c2410_nand_probe(%p)/n", pdev);</p>
<p></p>
<p>info = kmalloc(sizeof(*info), GFP_KERNEL); //分配s3c2410_nand_info内存</p>
<p>if (info == NULL) {</p>
<p>dev_err(&amp;pdev-&gt;dev, "no memory for flash info/n");</p>
<p>err = -ENOMEM;</p>
<p>goto exit_error;</p>
<p>}</p>
<p></p>
<p>memzero(info, sizeof(*info)); //将s3c2410_nand_info清零</p>
<p>platform_set_drvdata(pdev, info); //pdev-&gt;dev-&gt;driver_data = info</p>
<p></p>
<p>spin_lock_init(&amp;info-&gt;controller.lock);</p>
<p>init_waitqueue_head(&amp;info-&gt;controller.wq);</p>
<p></p>
<p></p>
<p></p>
<p>info-&gt;clk = clk_get(&amp;pdev-&gt;dev, "nand");</p>
<p>if (IS_ERR(info-&gt;clk)) {</p>
<p>dev_err(&amp;pdev-&gt;dev, "failed to get clock/n");</p>
<p>err = -ENOENT;</p>
<p>goto exit_error;</p>
<p>}</p>
<p></p>
<p>clk_enable(info-&gt;clk);</p>
<p></p>
<p></p>
<p></p>
<p></p>
<p>res = pdev-&gt;resource;</p>
<p>size = res-&gt;end - res-&gt;start + 1;</p>
<p></p>
<p>info-&gt;area = request_mem_region(res-&gt;start, size, pdev-&gt;name);</p>
<p></p>
<p>if (info-&gt;area == NULL) {</p>
<p>dev_err(&amp;pdev-&gt;dev, "cannot reserve register region/n");</p>
<p>err = -ENOENT;</p>
<p>goto exit_error;</p>
<p>}</p>
<p></p>
<p>info-&gt;device = &amp;pdev-&gt;dev;</p>
<p>info-&gt;platform = plat;</p>
<p>info-&gt;regs = ioremap(res-&gt;start, size); //存储nand控制器寄存器虚拟地</p>
<p>址</p>
<p>info-&gt;cpu_type = cpu_type;</p>
<p></p>
<p>if (info-&gt;regs == NULL) {</p>
<p>dev_err(&amp;pdev-&gt;dev, "cannot reserve register region/n");</p>
<p>err = -EIO;</p>
<p>goto exit_error;</p>
<p>}</p>
<p></p>
<p>dev_dbg(&amp;pdev-&gt;dev, "mapped registers at %p/n", info-&gt;regs);</p>
<p></p>
<p></p>
<p></p>
<p>err = s3c2410_nand_inithw(info, pdev); //设置TACLS TWRPH0 TWRPH1</p>
<p>if (err != 0)</p>
<p>goto exit_error;</p>
<p></p>
<p>sets = (plat != NULL) ? plat-&gt;sets : NULL; //sets指向plat-&gt;sets数组的首地址</p>
<p>nr_sets = (plat != NULL) ? plat-&gt;nr_sets : 1; //plat-&gt;sets中的chips数目</p>
<p></p>
<p>info-&gt;mtd_count = nr_sets;</p>
<p>size = nr_sets * sizeof(*info-&gt;mtds); </p>
<p>info-&gt;mtds = kmalloc(size, GFP_KERNEL);</p>
<p>if (info-&gt;mtds == NULL) {</p>
<p>dev_err(&amp;pdev-&gt;dev, "failed to allocate mtd storage/n");</p>
<p>err = -ENOMEM;</p>
<p>goto exit_error;</p>
<p>}</p>
<p>memzero(info-&gt;mtds, size); //将申请的s3c2410_nand_mtd结构体数组清零</p>
<p>nmtd = info-&gt;mtds;</p>
<p>for (setno = 0; setno &lt; nr_sets; setno++, nmtd++) { </p>
<p>pr_debug("initialising set %d (%p, info %p)/n", setno, nmtd, info);</p>
<p></p>
<p>s3c2410_nand_init_chip(info, nmtd, sets); //初始化s3c2410_nand_mtd结构</p>
<p>体中的chip成员和mtd成员,且mtd.priv = chip</p>
<p></p>
<p>nmtd-&gt;scan_res = nand_scan_ident(&amp;nmtd-&gt;mtd,</p>
<p>(sets) ? sets-&gt;nr_chips : 1); //设置nand_chip一些成员</p>
<p>的默认值并探测FLASH,并读出FLASH参数,填入nand_chip</p>
<p></p>
<p>if (nmtd-&gt;scan_res == 0) {</p>
<p>s3c2410_nand_update_chip(info, nmtd); //</p>
<p>nand_scan_tail(&amp;nmtd-&gt;mtd); //设置nand_chip中所有未被设置的</p>
<p>函数指针的值,并填充相关mtd_info成员,若需要建立bad block table</p>
<p>s3c2410_nand_add_partition(info, nmtd, sets); //添加分区</p>
<p>}</p>
<p></p>
<p>if (sets != NULL)</p>
<p>sets++; //注意这里sets++,指向下一个plat-&gt;sets里的set</p>
<p>}</p>
<p></p>
<p>if (allow_clk_stop(info)) {</p>
<p>dev_info(&amp;pdev-&gt;dev, "clock idle support enabled/n");</p>
<p>clk_disable(info-&gt;clk);</p>
<p>}</p>
<p></p>
<p>pr_debug("initialised ok/n");</p>
<p>return 0;</p>
<p></p>
<p>exit_error:</p>
<p>s3c2410_nand_remove(pdev);</p>
<p></p>
<p>if (err == 0)</p>
<p>err = -EINVAL;</p>
<p>return err;</p>
<p>}</p>
<p></p>
<p></p>
<p>附:几个机构体</p>
<img src="http://www.linuxidc.com/upload/2011_01/11011418309540.jpg" alt="Linux NAND FLASH驱动程序框架分析" align="middle"><img src="http://www.linuxidc.com/upload/2011_01/11011418301662.jpg" alt="Linux NAND FLASH驱动程序框架分析" align="middle"><img src="http://www.linuxidc.com/upload/2011_01/11011418309306.jpg" alt="Linux NAND FLASH驱动程序框架分析" align="middle"><img src="http://www.linuxidc.com/upload/2011_01/11011418309580.jpg" alt="Linux NAND FLASH驱动程序框架分析" align="middle"><br><br><br><textarea cols="50" rows="15" name="code" class="c-sharp">struct mtd_info {
u_char type;
u_int32_t flags;
u_int32_t size;  // Total size of the MTD


u_int32_t erasesize;

u_int32_t writesize;

u_int32_t oobsize;   // Amount of OOB data per block (e.g. 16)
u_int32_t oobavail;  // Available OOB bytes per block

// Kernel-only stuff starts here.
char *name;
int index;


struct nand_ecclayout *ecclayout;


int numeraseregions;
struct mtd_erase_region_info *eraseregions;


int (*erase) (struct mtd_info *mtd, struct erase_info *instr);



int (*point) (struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, void **virt, resource_size_t *phys);


void (*unpoint) (struct mtd_info *mtd, loff_t from, size_t len);


int (*read) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
int (*write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);



int (*panic_write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);

int (*read_oob) (struct mtd_info *mtd, loff_t from,
struct mtd_oob_ops *ops);
int (*write_oob) (struct mtd_info *mtd, loff_t to,
struct mtd_oob_ops *ops);


int (*get_fact_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);
int (*read_fact_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
int (*get_user_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);
int (*read_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
int (*write_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
int (*lock_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len);


int (*writev) (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen);


void (*sync) (struct mtd_info *mtd);


int (*lock) (struct mtd_info *mtd, loff_t ofs, size_t len);
int (*unlock) (struct mtd_info *mtd, loff_t ofs, size_t len);


int (*suspend) (struct mtd_info *mtd);
void (*resume) (struct mtd_info *mtd);


int (*block_isbad) (struct mtd_info *mtd, loff_t ofs);
int (*block_markbad) (struct mtd_info *mtd, loff_t ofs);

struct notifier_block reboot_notifier;


struct mtd_ecc_stats ecc_stats;

int subpage_sft;

void *priv;

struct module *owner;
int usecount;


int (*get_device) (struct mtd_info *mtd);
void (*put_device) (struct mtd_info *mtd);
};

struct nand_chip {
void  __iomem *IO_ADDR_R;
void  __iomem *IO_ADDR_W;

uint8_t (*read_byte)(struct mtd_info *mtd);
u16 (*read_word)(struct mtd_info *mtd);
void (*write_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);
void (*read_buf)(struct mtd_info *mtd, uint8_t *buf, int len);
int (*verify_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);
void (*select_chip)(struct mtd_info *mtd, int chip);
int (*block_bad)(struct mtd_info *mtd, loff_t ofs, int getchip);
int (*block_markbad)(struct mtd_info *mtd, loff_t ofs);
void (*cmd_ctrl)(struct mtd_info *mtd, int dat,
    unsigned int ctrl);
int (*dev_ready)(struct mtd_info *mtd);
void (*cmdfunc)(struct mtd_info *mtd, unsigned command, int column, int page_addr);
int (*waitfunc)(struct mtd_info *mtd, struct nand_chip *this);
void (*erase_cmd)(struct mtd_info *mtd, int page);
int (*scan_bbt)(struct mtd_info *mtd);
int (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state, int status, int page);
int (*write_page)(struct mtd_info *mtd, struct nand_chip *chip,
      const uint8_t *buf, int page, int cached, int raw);

int chip_delay;
unsigned int options;

int page_shift;
int phys_erase_shift;
int bbt_erase_shift;
int chip_shift;
int numchips;
unsigned long chipsize;
int pagemask;
int pagebuf;
int subpagesize;
uint8_t cellinfo;
int badblockpos;

nand_state_t state;

uint8_t *oob_poi;
struct nand_hw_control  *controller;
struct nand_ecclayout *ecclayout;

struct nand_ecc_ctrl ecc;
struct nand_buffers *buffers;
struct nand_hw_control hwcontrol;

struct mtd_oob_ops ops;

uint8_t *bbt;
struct nand_bbt_descr *bbt_td;
struct nand_bbt_descr *bbt_md;

struct nand_bbt_descr *badblock_pattern;

void *priv;
}; </textarea><br><br>
</div>
</div>
</div>

你可能感兴趣的:(linux)