精华内容
下载资源
问答
  • 浅析 mtd 子系统

    千次阅读 2016-10-10 23:20:16
    以 nandflash 驱动程序为例,简单分析一下内核中的 MTD 子系统。前面分析过块设备驱动程序,一个块设备需要用一个 gendisk 结构体来描述,还要提供 I/O 请求队列和 I/O 请求处理函数。如果按照这个思路的话,nand...

      以 nandflash 驱动程序为例,简单分析一下内核中的 MTD 子系统。前面分析过块设备驱动程序,一个块设备需要用一个 gendisk 结构体来描述,还要提供 I/O 请求队列和 I/O 请求处理函数。如果按照这个思路的话,nandflash 驱动程序我们就需要在请求处理函数中实现对 nandflash 的读写操作,事实上确实如此,只不过内核在块设备驱动的基础上又封装了一层,也就是所谓的MTD,现在只需要创建并填充一个 mtd_info 然后 add_mtd_partitions ,内核就会帮我们构造 gendisk 以及请求队列处理函数(它是个通用接口,最终还会调用的 mtd_info 芯片相关的函数)。

      本文以 nandflash 为例,简单分析 MTD 子系统的来龙去脉。首先来看一下两个简单的驱动程序:

    nandflash驱动:

    /* 参考 
     * drivers\mtd\nand\s3c2410.c
     * drivers\mtd\nand\at91_nand.c
     */
    
    #include <linux/module.h>
    #include <linux/types.h>
    #include <linux/init.h>
    #include <linux/kernel.h>
    #include <linux/string.h>
    #include <linux/ioport.h>
    #include <linux/platform_device.h>
    #include <linux/delay.h>
    #include <linux/err.h>
    #include <linux/slab.h>
    #include <linux/clk.h>
     
    #include <linux/mtd/mtd.h>
    #include <linux/mtd/nand.h>
    #include <linux/mtd/nand_ecc.h>
    #include <linux/mtd/partitions.h>
     
    #include <asm/io.h>
     
    #include <asm/arch/regs-nand.h>
    #include <asm/arch/nand.h>
    
    struct s3c_nand_regs {
    	unsigned long nfconf  ;
    	unsigned long nfcont  ;
    	unsigned long nfcmd   ;
    	unsigned long nfaddr  ;
    	unsigned long nfdata  ;
    	unsigned long nfeccd0 ;
    	unsigned long nfeccd1 ;
    	unsigned long nfeccd  ;
    	unsigned long nfstat  ;
    	unsigned long nfestat0;
    	unsigned long nfestat1;
    	unsigned long nfmecc0 ;
    	unsigned long nfmecc1 ;
    	unsigned long nfsecc  ;
    	unsigned long nfsblk  ;
    	unsigned long nfeblk  ;
    };
    
    
    static struct nand_chip *s3c_nand;
    static struct mtd_info *s3c_mtd;
    static struct s3c_nand_regs *s3c_nand_regs;
    
    static struct mtd_partition s3c_nand_parts[] = {
    	[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,
    	}
    };
    
    
    static void s3c2440_select_chip(struct mtd_info *mtd, int chipnr)
    {
    	if (chipnr == -1)
    	{
    		/* 取消选中: NFCONT[1]设为1 */
    		s3c_nand_regs->nfcont |= (1<<1);		
    	}
    	else
    	{
    		/* 选中: NFCONT[1]设为0 */
    		s3c_nand_regs->nfcont &= ~(1<<1);
    	}
    }
    
    static void s3c2440_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl)
    {
    	if (ctrl & NAND_CLE)
    	{
    		/* 发命令: NFCMMD=dat */
    		s3c_nand_regs->nfcmd = dat;
    	}
    	else
    	{
    		/* 发地址: NFADDR=dat */
    		s3c_nand_regs->nfaddr = dat;
    	}
    }
    
    static int s3c2440_dev_ready(struct mtd_info *mtd)
    {
    	return (s3c_nand_regs->nfstat & (1<<0));
    }
    
    
    static int s3c_nand_init(void)
    {
    	struct clk *clk;
    	
    	/* 1. 分配一个nand_chip结构体 */
    	s3c_nand = kzalloc(sizeof(struct nand_chip), GFP_KERNEL);
    
    	s3c_nand_regs = ioremap(0x4E000000, sizeof(struct s3c_nand_regs));
    	
    	/* 2. 设置nand_chip */
    	/* 设置nand_chip是给nand_scan函数使用的, 如果不知道怎么设置, 先看nand_scan怎么使用 
    	 * 它应该提供:选中,发命令,发地址,发数据,读数据,判断状态的功能
    	 */
    	s3c_nand->select_chip = s3c2440_select_chip;
    	s3c_nand->cmd_ctrl    = s3c2440_cmd_ctrl;
    	s3c_nand->IO_ADDR_R   = &s3c_nand_regs->nfdata;
    	s3c_nand->IO_ADDR_W   = &s3c_nand_regs->nfdata;
    	s3c_nand->dev_ready   = s3c2440_dev_ready;
    	s3c_nand->ecc.mode    = NAND_ECC_SOFT;
    	
    	/* 3. 硬件相关的设置: 根据NAND FLASH的手册设置时间参数 */
    	/* 使能NAND FLASH控制器的时钟 */
    	clk = clk_get(NULL, "nand");
    	clk_enable(clk);              /* CLKCON'bit[4] */
    	
    	/* HCLK=100MHz
    	 * TACLS:  发出CLE/ALE之后多长时间才发出nWE信号, 从NAND手册可知CLE/ALE与nWE可以同时发出,所以TACLS=0
    	 * TWRPH0: nWE的脉冲宽度, HCLK x ( TWRPH0 + 1 ), 从NAND手册可知它要>=12ns, 所以TWRPH0>=1
    	 * TWRPH1: nWE变为高电平后多长时间CLE/ALE才能变为低电平, 从NAND手册可知它要>=5ns, 所以TWRPH1>=0
    	 */
    #define TACLS    0
    #define TWRPH0   1
    #define TWRPH1   0
    	s3c_nand_regs->nfconf = (TACLS<<12) | (TWRPH0<<8) | (TWRPH1<<4);
    
    	/* NFCONT: 
    	 * BIT1-设为1, 取消片选 
    	 * BIT0-设为1, 使能NAND FLASH控制器
    	 */
    	s3c_nand_regs->nfcont = (1<<1) | (1<<0);
    	
    	/* 4. 使用: nand_scan */
    	s3c_mtd = kzalloc(sizeof(struct mtd_info), GFP_KERNEL);
    	s3c_mtd->owner = THIS_MODULE;
    	s3c_mtd->priv  = s3c_nand;
    	
    	nand_scan(s3c_mtd, 1);  /* 识别NAND FLASH, 构造mtd_info */
    	
    	/* 5. add_mtd_partitions */
    	 (s3c_mtd, s3c_nand_parts, 4);
    	
    	//add_mtd_device(s3c_mtd);
    	return 0;
    }
    
    static void s3c_nand_exit(void)
    {
    	del_mtd_partitions(s3c_mtd);
    	kfree(s3c_mtd);
    	iounmap(s3c_nand_regs);
    	kfree(s3c_nand);
    }
    
    module_init(s3c_nand_init);
    module_exit(s3c_nand_exit);
    
    MODULE_LICENSE("GPL");
    norflash驱动:
    /*
     * 参考 drivers\mtd\maps\physmap.c
     */
    
    #include <linux/module.h>
    #include <linux/types.h>
    #include <linux/kernel.h>
    #include <linux/init.h>
    #include <linux/slab.h>
    #include <linux/device.h>
    #include <linux/platform_device.h>
    #include <linux/mtd/mtd.h>
    #include <linux/mtd/map.h>
    #include <linux/mtd/partitions.h>
    #include <asm/io.h>
    
    static struct map_info *s3c_nor_map;
    static struct mtd_info *s3c_nor_mtd;
    
    static struct mtd_partition s3c_nor_parts[] = {
    	[0] = {
            .name   = "bootloader_nor",
            .size   = 0x00040000,
    		.offset	= 0,
    	},
    	[1] = {
            .name   = "root_nor",
            .offset = MTDPART_OFS_APPEND,
            .size   = MTDPART_SIZ_FULL,
    	}
    };
    
    
    static int s3c_nor_init(void)
    {
    	/* 1. 分配map_info结构体 */
    	s3c_nor_map = kzalloc(sizeof(struct map_info), GFP_KERNEL);;
    	
    	/* 2. 设置: 物理基地址(phys), 大小(size), 位宽(bankwidth), 虚拟基地址(virt) */
    	s3c_nor_map->name = "s3c_nor";
    	s3c_nor_map->phys = 0;
    	s3c_nor_map->size = 0x1000000; /* >= NOR的真正大小 */
    	s3c_nor_map->bankwidth = 2;
    	s3c_nor_map->virt = ioremap(s3c_nor_map->phys, s3c_nor_map->size);
    
    	simple_map_init(s3c_nor_map);
    	
    	/* 3. 使用: 调用NOR FLASH协议层提供的函数来识别 */
    	printk("use cfi_probe\n");
    	s3c_nor_mtd = do_map_probe("cfi_probe", s3c_nor_map);
    	if (!s3c_nor_mtd)
    	{
    		printk("use jedec_probe\n");
    		s3c_nor_mtd = do_map_probe("jedec_probe", s3c_nor_map);
    	}
    
    	if (!s3c_nor_mtd)
    	{		
    		iounmap(s3c_nor_map->virt);
    		kfree(s3c_nor_map);
    		return -EIO;
    	}
    	
    	/* 4. add_mtd_partitions */
    	add_mtd_partitions(s3c_nor_mtd, s3c_nor_parts, 2);
    	
    	return 0;
    }
    
    static void s3c_nor_exit(void)
    {
    	del_mtd_partitions(s3c_nor_mtd);
    	iounmap(s3c_nor_map->virt);
    	kfree(s3c_nor_map);
    }
    
    module_init(s3c_nor_init);
    module_exit(s3c_nor_exit);
    
    MODULE_LICENSE("GPL");
    
      经过对比不难发现,nandflash 和 norflash 驱动最终都会调用 add_mtd_partitions ,传递两个参数一个是 mtd_info 结构,另一个是分区信息相关的 mtd_partition 结构。不一样的是在 add_mtd_partitions 之前,它们 mtd_info 填充的方式不一样,但也类似。对于 nandflash 来说,填充 mtd_info 部分和 uboot2012 中的 nandflash 识别部分一模一样,之前这部分我已经在前边的移植 uboot2012 之 nandflash 识别中分析过了,这里就不再赘述,大致的过程如下:
    s3c_nand->select_chip = s3c2440_select_chip;
    s3c_nand->cmd_ctrl    = s3c2440_cmd_ctrl;
    s3c_nand->IO_ADDR_R   = &s3c_nand_regs->nfdata;
    s3c_nand->IO_ADDR_W   = &s3c_nand_regs->nfdata;
    s3c_nand->dev_ready   = s3c2440_dev_ready;
    s3c_nand->ecc.mode    = NAND_ECC_SOFT;
    nand_scan  // drivers/mtd/nand/nand_base.c 根据nand_chip的底层操作函数识别NAND FLASH,构造mtd_info
        nand_scan_ident
            nand_set_defaults
                if (!chip->select_chip)
                    chip->select_chip = nand_select_chip; // 默认值不适用
                    if (chip->cmdfunc == NULL)
                        chip->cmdfunc = nand_command;
                        chip->cmd_ctrl(mtd, command, ctrl);
                    if (!chip->read_byte)
                        chip->read_byte = nand_read_byte;
                        readb(chip->IO_ADDR_R);
                        if (chip->waitfunc == NULL)
                            chip->waitfunc = nand_wait;
                            chip->dev_ready
                   
            nand_get_flash_type
                chip->select_chip(mtd, 0);
                chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
                *maf_id = chip->read_byte(mtd);
                dev_id = chip->read_byte(mtd);
        nand_scan_tail
            mtd->erase = nand_erase;
            mtd->read = nand_read;
            mtd->write = nand_write;
    
    下面来看 MTD 子系统相关部分:
    static int __init init_mtdblock(void)
    {
    	mutex_init(&mtdblks_lock);
    
    	return register_mtd_blktrans(&mtdblock_tr);
    }
    module_init(init_mtdblock);
    在内核启动时,注册了这么一个块设备通用操作结构体:
    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,
    };

    register_mtd_blktrans(&mtdblock_tr);

    int register_mtd_blktrans(struct mtd_blktrans_ops *tr)
    {
    
    	if (!blktrans_notifier.list.next)
    		register_mtd_user(&blktrans_notifier);
    
    	tr->blkcore_priv = kzalloc(sizeof(*tr->blkcore_priv), GFP_KERNEL);
    	// 注册块设备号
    	ret = register_blkdev(tr->major, tr->name);
    	// 自旋锁
    	spin_lock_init(&tr->blkcore_priv->queue_lock);
    	// 设备块设备通用 请求队列
    	tr->blkcore_priv->rq = blk_init_queue(mtd_blktrans_request, &tr->blkcore_priv->queue_lock);
    	// 请求处理函数在 mtd_blktrans_thread 线程
    	tr->blkcore_priv->thread = kthread_run(mtd_blktrans_thread, tr,
    			"%sd", tr->name);
    		
    	INIT_LIST_HEAD(&tr->devs);
    	// 将 mtdblock_tr 加入全局链表 blktrans_majors
    	list_add(&tr->list, &blktrans_majors);
    }
      这不就是前面块设备驱动程序中干的那些工作么,blk_init_queue 初始化一个请求队列,并设置请求处理函数,只不过内核这里搞的是对所有的块设备通用的接口函数。

    register_mtd_user(&blktrans_notifier);

    // register_mtd_user(&blktrans_notifier);
    // static LIST_HEAD(mtd_notifiers);
    void register_mtd_user (struct mtd_notifier *new)
    {
    	// 将 blktrans_notifier 加入全局链表 mtd_notifiers 
    	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 mtd_blktrans_ops *tr;
    
    	if (mtd->type == MTD_ABSENT)
    		return;
    
    	list_for_each_entry(tr, &blktrans_majors, list)
    		// mtdblock_add_mtd(tr, mtd)
    		tr->add_mtd(tr, mtd);
    }
    mtd_notifiers 成为对外接口
    mtd_notifiers -> blktrans_notifier -> blktrans_notify_add -> blktrans_majors -> mtdblock_tr

        add_mtd_partitions
            add_mtd_device
                list_for_each(this, &mtd_notifiers) { 
                    struct mtd_notifier *not = list_entry(this, struct mtd_notifier, list);
                    not->add(mtd);//<span style="font-family: FangSong_GB2312;font-size:18px;">mtdblock_tr->add</span>

    mtdblock_add_mtd

    static void mtdblock_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
    {
    	struct mtd_blktrans_dev *dev = kzalloc(sizeof(*dev), GFP_KERNEL);
    
    	dev->mtd = mtd;
    	dev->devnum = mtd->index;
    
    	dev->size = mtd->size >> 9;
    	dev->tr = tr;
    
    	add_mtd_blktrans_dev(dev);
    }
    int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new)
    {
    	struct mtd_blktrans_ops *tr = new->tr;
    	struct mtd_blktrans_dev *d;
    	int last_devnum = -1;
    	// gendisk 结构体
    	struct gendisk *gd;
    
    	gd = alloc_disk(1 << tr->part_bits);
    
    	gd->major = tr->major;
    	gd->first_minor = (new->devnum) << tr->part_bits;
    	gd->fops = &mtd_blktrans_ops;
    
    	set_capacity(gd, (new->size * tr->blksize) >> 9);
    
    	gd->private_data = new;
    	new->blkcore_priv = gd;
    	// tr->blkcore_priv->rq = blk_init_queue(mtd_blktrans_request, &tr->blkcore_priv->queue_lock);
    	gd->queue = tr->blkcore_priv->rq;
    	gd->driverfs_dev = &new->mtd->dev;
    
    	if (new->readonly)
    		set_disk_ro(gd, 1);
    	
    	// add_disk
    	add_disk(gd);
    
    	return 0;
    }
      块设备的 gendisk 结构出现了,mtd_blktrans_request 是通用的请求处理函数接口
    static void mtd_blktrans_request(struct request_queue *rq)
    {
    	struct mtd_blktrans_ops *tr = rq->queuedata;
    	// tr->blkcore_priv->thread = kthread_run(mtd_blktrans_thread, tr,"%sd", tr->name);
    	wake_up_process(tr->blkcore_priv->thread);
    }
    
    static int mtd_blktrans_thread(void *arg){
    	...
    	res = do_blktrans_request(tr, dev, req);
    }
    static int do_blktrans_request(struct mtd_blktrans_ops *tr,
    			       struct mtd_blktrans_dev *dev,
    			       struct request *req)
    {
    	unsigned long block, nsect;
    	char *buf;
    
    	block = blk_rq_pos(req) << 9 >> tr->blkshift;
    	nsect = blk_rq_cur_bytes(req) >> tr->blkshift;
    
    	buf = req->buffer;
    
    	if (!blk_fs_request(req))
    		return -EIO;
    
    	if (blk_rq_pos(req) + blk_rq_cur_sectors(req) >
    	    get_capacity(req->rq_disk))
    		return -EIO;
    
    	if (blk_discard_rq(req))
    		return tr->discard(dev, block, nsect);
    
    	switch(rq_data_dir(req)) {
    	case READ:
    		for (; nsect > 0; nsect--, block++, buf += tr->blksize)
    			// mtdblock_writesect
    			// if (dev->mtd->write(dev->mtd, (block * 512), 512, &retlen, buf))
    			if (tr->readsect(dev, block, buf))
    				return -EIO;
    		return 0;
    
    	case WRITE:
    		if (!tr->writesect)
    			return -EIO;
    
    		for (; nsect > 0; nsect--, block++, buf += tr->blksize)
    			if (tr->writesect(dev, block, buf))
    				return -EIO;
    		return 0;
    
    	default:
    		printk(KERN_NOTICE "Unknown request %u\n", rq_data_dir(req));
    		return -EIO;
    	}
    }









    展开全文
  • MTD子系统-mtdcore.c分析

    2012-11-25 11:41:03
    MTD子系统-mtdcore.c分析
  • MTD子系统架构 mtd设备注册流程

    MTD子系统架构

    在这里插入图片描述

    mtd设备注册流程

    在这里插入图片描述

    展开全文
  • 从今天开始,我们进行linux mtd子系统的分析。mtd子系统即为内存技术设备子系统,主要包括nor flash、nand flash等闪存设备相关的子系统模块,而针对sd、tf等存储设备,则主要由mmc子系统模块进行管理并创建对应的块...

          从今天开始,我们进行linux mtd子系统的分析。mtd子系统即为内存技术设备子系统,主要包括nor flash、nand flash等闪存设备相关的子系统模块,而针对sd、tf等存储设备,则主要由mmc子系统模块进行管理并创建对应的块设备。而针对mtd子系统,则会基于mtd原始设备,创建mtd字符设备与mtd块设备,从而在上层与vfs系统进行关联。

     

    针对mtd子系统,主要涉及如下几个方面的介绍:

    一、MTD子系统概述

    二、MTD设备驱动模型相关数据结构说明

    三、MTD块设备与字符设备

    四、MTD上层接口与nor/nandflash驱动的关联关系

    五、nandflash驱动介绍

    六、norflash驱动简要介绍

    七、spi nor flash驱动简要介绍

    八、应用层完成对flash中存储的镜像升级功能

     

     

    本篇主要介绍MTD子系统的概念,并简要说明mtd子系统的架构,主要包括mtd子系统与上层文件系统、与底层驱动间的关联等。

     

     

    设备间的逻辑关联图与抽象

        下图是norflash 、nandflash与cpu之间的关联。针对norflash,可直接与cpu关联,也可通过spi controller与cpu相连;而针对nandflash,即可以通过nandflash controller、spi controlloer与cpu相连。

     

          而针对spi controller、spi device、spi driver的抽象及关联,已在之前的专栏中介绍。而针对nandflash controller,我们在后面会详细说明。而mtd驱动模型则通过对nor flash、nandflash等闪存设备进行抽象,对上层模块抽象成统一成设备模型(mtd_info),对下则通过mtd_info完成与具体闪存设备驱动的绑定,从而完成对闪存设备的访问操作。此处mtd的抽象,就和vfs抽象类似。

     

     

     

     

    MTD设备驱动模型与VFS及FLASH驱动的关联

    如下为mtd设备驱动模型、vfs、flash设备驱动之间的关联图。

          针对mtd驱动模型,其对上关联文件系统、对下则主要关联具体的存储驱动。它们之间的关联图

    如下所示。

          其中mtd block、mtd char均属于mtd驱动模型的部分,而FTL、NFTL则不属于mtd驱动模型部分 ,由具体的文件系统来实现,如ubi文件系统挂载时,则是通过设备文件/dev/ubiX_Y进行挂载;而针对yaffs2文件系统,其可以直接使用/dev/mtdblockX进行挂载(当然其也可以不借助mtdblock)。而针对mtd char,主要实现对相应的flash设备进行顺序访问,那mtd char可具体实现什么功能

    呢?

         可能的应用一:当我们想在文件系统下实现对bootloader、kernel、文件系统镜像的升级操作时,则可以通过读写/dev/mtdX(mtd 字符设备),实现针对bootloader、kernel、filesystem的升级操作。

     

          针对mtd block、mtd char,其均是同调用mtd_info(即mtd设备),实现对具体flash芯片的读写操作的。

           而mtd_info则代表一个flash芯片或一个flash芯片的分区,当flash设备(nandflash设备、nor flash设备)的驱动初始化时,则会根据分区划分,创建对应的mtd_info,并完成mtd_info与该芯片驱动的绑定操作。而在mtd_info的创建过程中,则会创建对应的mtd char、mtd block,从而完成与vfs的关联。

     

       本篇主要对mtd设备驱动模型做一个大致的概述(已经一个多月没有更新了,人忽然间就懒下来了,本篇即是开个头,以便督促自己继续更新)。

     

    展开全文
  • 转载:Linux MTD子系统 _从模型分析到Flash驱动模板 MTD(Memory Technology Device)即常说的Flash等使用存储芯片的存储设备,MTD子系统对应的是块设备驱动框架中的设备驱动层,可以说,MTD就是针对Flash设备设计的...

    转载:Linux MTD子系统 _从模型分析到Flash驱动模板

    MTD(Memory Technology Device)即常说的Flash等使用存储芯片的存储设备,MTD子系统对应的是块设备驱动框架中的设备驱动层,可以说,MTD就是针对Flash设备设计的标准化硬件驱动框架。本文基于3.14内核,讨论MTD驱动框架。

    MTD子系统框架

    • 设备节点层:MTD框架可以在/dev下创建字符设备节点(主设备号90)以及块设备节点(主设备号31), 用户通过访问此设备节点即可访问MTD字符设备或块设备。
    • MTD设备层: 基于MTD原始设备, Linux在这一层次定义出了MTD字符设备和块设备, 字符设备在mtdchar.c中实现, 块设备则是通过结构mtdblk_dev来描述,"/drivers/mtd/mtdchar.c"文件实现了MTD字符设备接口; "/drivers/mtd/mtdblock.c"文件实现了MTD块设备接口
    • MTD原始设备层: 由MTD原始设备的通用代码+特定的Flash数据组成。mtd_info、mtd_part、mtd_partition以及mtd_partitions等对象及其操作方法就属于这一层,对应的文件是"drivers/mtd/mtdcore.c"。类似于i2c驱动框架中的核心层。
    • 硬件驱动层: 内核将常用的flash操作都已经在这个层次实现, 驱动开发只需要将相应的设备信息添加进去即可, 比如,NOR flash的芯片驱动位于"drivers/mtd/chips/", Nand flash位于"drivers/mtd/nand/"(eg s3c2410.c)

    核心结构和方法简述

    为了实现上述的框架, 内核中使用了如下类和API, 这些几乎是开发一个MTD驱动必须的

    核心结构

    • mtd_info描述原始设备层的一个分区的结构, 描述一个设备或一个多分区设备中的一个分区
    • mtd_table管理原始设备层的mtd_info的数组
    • mtd_part表示一个分区, 其中的struct mtd_info mtd描述该分区的信息, 一个物理Flash设备可以有多于1个mtd_part,每个mtd_part都对应一个mtd_info。
    • mtd_partition描述一个分区表, 通过管理mtd_part以及每一个mtd_part中的mtd_info来描述所有的分区,一个物理Flash设备只有一个mtd_partition
    • mtd_partitions是一个list_head对象,用于管理mtd_partition们
    • map_info描述一个NOR Flash设备
    • nand_chip描述一个NAND Flash设备

    核心方法

    • add_mtd_device()/del_mtd_device()注册/注销一个MTD设备
    • add_mtd_partitions()/del_mtd_partitions()注册注销一个或多个分区表,
    • do_map_probe()用来根据传入的参数匹配一个map_info对象的驱动,比如CFI接口或JEDEC接口的NOR Flash,并返回一个mtd_info以便注册分区信息。
    • nand_scan():NAND flash使用这个API来匹配驱动。

    核心结构与方法详述

    mtd_info

    本身是没有list_head来供内核管理,对mtd_info对象的管理是通过mtd_part来实现的。mtd_info对象属于原始设备层,里面的很多函数接口内核已经实现了。mtd_info中的read()/write()等操作是MTD设备驱动要实现的主要函数,在NORFlash或NANDFlash中的驱动代码中几乎看不到mtd_info的成员函数,即这些函数对于Flash芯片是透明的,因为Linux在MTD的下层实现了针对NORFlash和NANDFlash的通用的mtd_info函数。

    114 struct mtd_info {
    115         u_char type;
    116         uint32_t flags;
    117         uint64_t size;   // Total size of the MTD
    118 
    123         uint32_t erasesize;
    131         uint32_t writesize;
    132 
    142         uint32_t writebufsize;
    143 
    144         uint32_t oobsize;   // Amount of OOB data per block (e.g. 16)
    145         uint32_t oobavail;  // Available OOB bytes per block
    146 
    151         unsigned int erasesize_shift;
    152         unsigned int writesize_shift;
    153         /* Masks based on erasesize_shift and writesize_shift */
    154         unsigned int erasesize_mask;
    155         unsigned int writesize_mask;
    156 
    164         unsigned int bitflip_threshold;
    165 
    166         // Kernel-only stuff starts here.
    167         const char *name;
    168         int index;
    169 
    170         /* ECC layout structure pointer - read only! */ 
    171         struct nand_ecclayout *ecclayout;
    172 
    173         /* the ecc step size. */
    174         unsigned int ecc_step_size;
    175 
    176         /* max number of correctible bit errors per ecc step */
    177         unsigned int ecc_strength;
    178 
    179         /* Data for variable erase regions. If numeraseregions is zero,
    180          * it means that the whole device has erasesize as given above.
    181          */
    182         int numeraseregions;
    183         struct mtd_erase_region_info *eraseregions;
    184 
    185         /*
    186          * Do not call via these pointers, use corresponding mtd_*()
    187          * wrappers instead.
    188          */
    189         int (*_erase) (struct mtd_info *mtd, struct erase_info *instr);
    190         int (*_point) (struct mtd_info *mtd, loff_t from, size_t len,
    191                        size_t *retlen, void **virt, resource_size_t *phys);
    192         int (*_unpoint) (struct mtd_info *mtd, loff_t from, size_t len);
    193         unsigned long (*_get_unmapped_area) (struct mtd_info *mtd,
    194                                              unsigned long len,
    195                                              unsigned long offset,
    196                                              unsigned long flags);
    197         int (*_read) (struct mtd_info *mtd, loff_t from, size_t len,
    198                       size_t *retlen, u_char *buf);
    199         int (*_write) (struct mtd_info *mtd, loff_t to, size_t len,
    200                        size_t *retlen, const u_char *buf);
    201         int (*_panic_write) (struct mtd_info *mtd, loff_t to, size_t len,
    202                              size_t *retlen, const u_char *buf);
    203         int (*_read_oob) (struct mtd_info *mtd, loff_t from,
    204                           struct mtd_oob_ops *ops);
    205         int (*_write_oob) (struct mtd_info *mtd, loff_t to,
    206                            struct mtd_oob_ops *ops);
    207         int (*_get_fact_prot_info) (struct mtd_info *mtd, struct otp_info *buf,
    208                                     size_t len);
    209         int (*_read_fact_prot_reg) (struct mtd_info *mtd, loff_t from,
    210                                     size_t len, size_t *retlen, u_char *buf);
    211         int (*_get_user_prot_info) (struct mtd_info *mtd, struct otp_info *buf,
    212                                     size_t len);
    213         int (*_read_user_prot_reg) (struct mtd_info *mtd, loff_t from,
    214                                     size_t len, size_t *retlen, u_char *buf);
    215         int (*_write_user_prot_reg) (struct mtd_info *mtd, loff_t to,
    216                                      size_t len, size_t *retlen, u_char *buf);
    217         int (*_lock_user_prot_reg) (struct mtd_info *mtd, loff_t from,
    218                                     size_t len);
    219         int (*_writev) (struct mtd_info *mtd, const struct kvec *vecs,
    220                         unsigned long count, loff_t to, size_t *retlen);
    221         void (*_sync) (struct mtd_info *mtd);
    222         int (*_lock) (struct mtd_info *mtd, loff_t ofs, uint64_t len);
    223         int (*_unlock) (struct mtd_info *mtd, loff_t ofs, uint64_t len);
    224         int (*_is_locked) (struct mtd_info *mtd, loff_t ofs, uint64_t len);
    225         int (*_block_isbad) (struct mtd_info *mtd, loff_t ofs);
    226         int (*_block_markbad) (struct mtd_info *mtd, loff_t ofs);
    227         int (*_suspend) (struct mtd_info *mtd);
    228         void (*_resume) (struct mtd_info *mtd);
    229         /*
    230          * If the driver is something smart, like UBI, it may need to maintain
    231          * its own reference counting. The below functions are only for driver.
    232          */
    233         int (*_get_device) (struct mtd_info *mtd);
    234         void (*_put_device) (struct mtd_info *mtd);
    235 
    236         /* Backing device capabilities for this device
    237          * - provides mmap capabilities
    238          */
    239         struct backing_dev_info *backing_dev_info;
    240 
    241         struct notifier_block reboot_notifier;  /* default mode before reboot */
    242 
    243         /* ECC status information */
    244         struct mtd_ecc_stats ecc_stats;
    245         /* Subpage shift (NAND) */
    246         int subpage_sft;
    247 
    248         void *priv;
    249 
    250         struct module *owner;
    251         struct device dev;
    252         int usecount;
    253 };

    struct mtd_info
    --115-->MTD设备类型,有MTD_RAM,MTD_ROM、MTD_NORFLASH、MTD_NAND_FLASH
    --116-->读写及权限标志位,有MTD_WRITEABLE、MTD_BIT_WRITEABLE、MTD_NO_ERASE、MTD_UP_LOCK
    --117-->MTD设备的大小
    --123-->主要的擦除块大小,NandFlash就是"块"的大小
    --131-->最小可写字节数,NandFlash一般对应"页"的大小
    --144-->一个block中的OOB字节数
    --145-->一个block中可用oob的字节数
    --171-->ECC布局结构体指针
    --190-->针对eXecute-In-Place,即XIP
    --192-->如果这个指针为空,不允许XIP
    --197-->读函数指针
    --199-->写函数指针
    --248-->私有数据

    mtd_part

    内核管理分区的链表节点,通过它来实现对mtd_info对象的管理。

     41 struct mtd_part {
     42         struct mtd_info mtd;
     43         struct mtd_info *master;                                            
     44         uint64_t offset;
     45         struct list_head list;
     46 };

    struct mtd_part
    --42-->对应的mtd_info对象
    --43-->父对象指针
    --44-->偏移量
    --45-->链表节点

    mtd_partition

    描述一个分区

     39 struct mtd_partition {
     40         const char *name;               /* identifier string */
     41         uint64_t size;                  /* partition size */
     42         uint64_t offset;                /* offset within the master MTD space */                                                
     43         uint32_t mask_flags;            /* master MTD flags to mask out for this partition */
     44         struct nand_ecclayout *ecclayout;       /* out of band layout for this partition (NAND only) */
     45 };

    mtd_partition
    --40-->分区名
    --41-->分区大小,使用MTDPART_SIZ_FULL表示使用全部空间
    --42-->分区在master设备中的偏移量。MTDPART_OFS_APPEND表示从上一个分区结束的地方开始,MTDPART_OFS_NXTBLK表示从下一个擦除块开始; MTDPART_OFS_RETAIN表示尽可能向后偏,把size大小的空间留下即可
    --43-->权限掩码,MTD_WRITEABLE表示将父设备的只读选项变成可写(可写分区要求size和offset要erasesize对齐,eg MTDPART_OFS_NEXTBLK)
    --44-->NANDFlash的OOB布局,OOB是NANDFlash中很有用空间,比如yaffs2就需要将坏块信息存储在OOB区域

    mtd_partitions

    链表头,将所有的mtd_partition连接起来。

     36 /* Our partition linked list */
     37 static LIST_HEAD(mtd_partitions);  

    下图是关键API的调用关系。

    mtd_add_partition()
       └── add_mtd_device()
    add_mtd_partitions()
       └── add_mtd_device()

    add_mtd_device()

    分配并初始化一个mtd对象。

     367 334 int add_mtd_device(struct mtd_info *mtd)
     335 {
     336         struct mtd_notifier *not;
     337         int i, error;
     338 
     339         if (!mtd->backing_dev_info) {
     340                 switch (mtd->type) {
     341                 case MTD_RAM:
     342                         mtd->backing_dev_info = &mtd_bdi_rw_mappable;
     343                         break;
     344                 case MTD_ROM:
     345                         mtd->backing_dev_info = &mtd_bdi_ro_mappable;
     346                         break;
     347                 default:
     348                         mtd->backing_dev_info = &mtd_bdi_unmappable;
     349                         break;
     350                 }
     351         }
     355 
     356         i = idr_alloc(&mtd_idr, mtd, 0, 0, GFP_KERNEL);
     357         if (i < 0)
     358                 goto fail_locked;
     359 
     360         mtd->index = i;
     361         mtd->usecount = 0;
     362 
     363         /* default value if not set by driver */
     364         if (mtd->bitflip_threshold == 0)
     365                 mtd->bitflip_threshold = mtd->ecc_strength;
     366 
     367         if (is_power_of_2(mtd->erasesize))
     368                 mtd->erasesize_shift = ffs(mtd->erasesize) - 1;
     369         else
     370                 mtd->erasesize_shift = 0;
     371 
     372         if (is_power_of_2(mtd->writesize))                                                         
     373                 mtd->writesize_shift = ffs(mtd->writesize) - 1;
     374         else
     375                 mtd->writesize_shift = 0;
     376 
     377         mtd->erasesize_mask = (1 << mtd->erasesize_shift) - 1;
     378         mtd->writesize_mask = (1 << mtd->writesize_shift) - 1;
     379 
     380         /* Some chips always power up locked. Unlock them now */
     381         if ((mtd->flags & MTD_WRITEABLE) && (mtd->flags & MTD_POWERUP_LOCK)) {
     382                 error = mtd_unlock(mtd, 0, mtd->size);
     387         }
     388 
     392         mtd->dev.type = &mtd_devtype;
     393         mtd->dev.class = &mtd_class;
     394         mtd->dev.devt = MTD_DEVT(i);
     395         dev_set_name(&mtd->dev, "mtd%d", i);
     396         dev_set_drvdata(&mtd->dev, mtd);
     397         if (device_register(&mtd->dev) != 0)
     399 
     400         if (MTD_DEVT(i))
     401                 device_create(&mtd_class, mtd->dev.parent,
     402                               MTD_DEVT(i) + 1,
     403                               NULL, "mtd%dro", i);
     408         list_for_each_entry(not, &mtd_notifiers, list)
     409                 not->add(mtd);
     417         return 0;
     424 }

    add_mtd_device()
    --395-->设置MTD设备的名字
    --396-->设置私有数据,将mtd地址藏到device->device_private->void* driver_data
    --408-->遍历所有的mtd_notifier,将其添加到通知链

    mtd_add_partition()

    通过将一个mtd_part对象注册到内核,将mtd_info对象注册到内核,即为一个设备添加一个分区。

    537 int mtd_add_partition(struct mtd_info *master, const char *name,
    538                       long long offset, long long length)
    539 {
    540         struct mtd_partition part;
    541         struct mtd_part *p, *new;
    542         uint64_t start, end;
    543         int ret = 0;
    545         /* the direct offset is expected */
    546         if (offset == MTDPART_OFS_APPEND ||                                                         
    547             offset == MTDPART_OFS_NXTBLK)
    548                 return -EINVAL;
    549 
    550         if (length == MTDPART_SIZ_FULL)
    551                 length = master->size - offset;
    552 
    553         if (length <= 0)
    554                 return -EINVAL;
    555 
    556         part.name = name;
    557         part.size = length;
    558         part.offset = offset;
    559         part.mask_flags = 0;
    560         part.ecclayout = NULL;
    561 
    562         new = allocate_partition(master, &part, -1, offset);
    563         if (IS_ERR(new))
    564                 return PTR_ERR(new);
    565 
    566         start = offset;
    567         end = offset + length;
    568 
    569         mutex_lock(&mtd_partitions_mutex);
    570         list_for_each_entry(p, &mtd_partitions, list)
    571                 if (p->master == master) {
    572                         if ((start >= p->offset) &&
    573                             (start < (p->offset + p->mtd.size)))
    574                                 goto err_inv;
    575 
    576                         if ((end >= p->offset) &&
    577                             (end < (p->offset + p->mtd.size)))
    578                                 goto err_inv;
    579                 }
    580 
    581         list_add(&new->list, &mtd_partitions);
    582         mutex_unlock(&mtd_partitions_mutex);
    583 
    584         add_mtd_device(&new->mtd);
    585 
    586         return ret;
    591 }

    add_mtd_partitions()

    添加一个分区表到内核,一个MTD设备一个分区表

    626 int add_mtd_partitions(struct mtd_info *master,
    627                        const struct mtd_partition *parts,
    628                        int nbparts)
    629 {
    630         struct mtd_part *slave;
    631         uint64_t cur_offset = 0;
    632         int i;
    636         for (i = 0; i < nbparts; i++) {
    637                 slave = allocate_partition(master, parts + i, i, cur_offset);
    642                 list_add(&slave->list, &mtd_partitions);
    645                 add_mtd_device(&slave->mtd);
    647                 cur_offset = slave->offset + slave->mtd.size;
    648         }
    649 
    650         return 0;                                                                                   
    651 }

    用户空间编程

    MTD设备提供了字符设备和块设备两种接口,对于字符设备接口,在"drivers/mtd/mtdchar.c"中实现了,比如,用户程序可以直接通过ioctl()回调相应的驱动实现。其中下面的几个是这些操作中常用的结构,这些结构是对用户空间开放的,类似于输入子系统中的input_event结构。

    mtd_info_user

    //include/uapi/mtd/mtd-abi.h
    125 struct mtd_info_user {                                                                              
    126         __u8 type;
    127         __u32 flags;
    128         __u32 size;     /* Total size of the MTD */
    129         __u32 erasesize;    
    130         __u32 writesize;        
    131         __u32 oobsize;  /* Amount of OOB data per block (e.g. 16) */
    132         __u64 padding;  /* Old obsolete field; do not use */
    133 };

    mtd_oob_buf

    描述NandFlash的OOB(Out Of Band)信息。

     35 struct mtd_oob_buf {                                                                                
     36         __u32 start;
     37         __u32 length;
     38         unsigned char __user *ptr;
     39 };

    erase_info_user

     25 struct erase_info_user {
     26         __u32 start;
     27         __u32 length;
     28 };

    实例

    mtd_oob_buf oob;
    erase_info_user erase;
    mtd_info_user meminfo;
    
    /* 获得设备信息 */
    if(0 != ioctl(fd, MEMGETINFO, &meminfo))
        perror("MEMGETINFO");
        
    /* 擦除块 */
    if(0 != ioctl(fd, MEMERASE, &erase))
        perror("MEMERASE");
    
    /* 读OOB */
    if(0 != ioctl(fd, MEMREADOOB, &oob))
        perror("MEMREADOOB");
    
    /* 写OOB??? */    
    if(0 != ioctl(fd, MEMWRITEOOB, &oob))
        perror("MEMWRITEOOB");
        
    /* 检查坏块 */
    if(blockstart != (ofs & (~meminfo.erase + 1))){
        blockstart = ofs & (~meminfo.erasesize + 1);
        if((badblock = ioctl(fd, MEMGETBADBLOCK, &blockstart)) < 0)
            perror("MEMGETBADBLOCK");
        else if(badblock)
            /* 坏块代码 */
        else
            /* 好块代码 */
    }

    NANDFlash和NORFlash都是基于MTD框架编写的,由于MTD框架中通用代码已经在内核中实现了,所以驱动开发主要是进行MTD框架中的的开发。

    NOR Flash驱动

    下图就是NORFlash驱动在MTD驱动框架中的位置

    基于上述的MTD框架, Flash驱动都变的十分的简单, 因为当下Flash的操作接口已经很统一, a, 相应的代码在"drivers/mtd/chips"中文件实现,所以在设备驱动层, 留给驱动工程师的工作就大大的减少了。
    基于MTD子系统开发NOR FLash驱动,只需要构造一个map_info类型的对象并调用do_map_probe()来匹配内核中已经写好的驱动,比如CFI接口的驱动或JEDEC接口的驱动。当下编写一个NorFlash驱动的工作流程如下

    map_info

    208 struct map_info {
    209         const char *name;
    210         unsigned long size;
    211         resource_size_t phys;
    212 #define NO_XIP (-1UL)
    214         void __iomem *virt;
    215         void *cached;
    217         int swap; /* this mapping's byte-swapping requirement */
    218         int bankwidth; 
    243         void (*set_vpp)(struct map_info *, int);
    245         unsigned long pfow_base;
    246         unsigned long map_priv_1;
    247         unsigned long map_priv_2;
    248         struct device_node *device_node;
    249         void *fldrv_priv;
    250         struct mtd_chip_driver *fldrv;                                                              
    251 };

    struct map_info
    --210-->NOR Flash设备的容量
    --211-->NOR Flash在物理地址空间中的地址
    --214-->由物理地址映射的虚拟地址
    --218-->总线宽度,NOR Flash是有地址总线的,所以才能片上执行,一般都是8位或16位宽

    构造好一个map_info对象之后,接下来的工作就是匹配驱动+注册分区表

    do_map_probe()

    这个API用来根据传入的参数匹配一个map_info对象的驱动,比如CFI接口或JEDEC接口的NOR Flash。这个函数的接口如下:

    struct mtd_info *do_map_probe(const char *name, struct map_info *map)

    对于常用的NorFlash标准,这个函数的调用方式如下:

    do_map_probe("cfi_probe", &xxx_map_info);
    do_map_probe("jedec_probe",&xxx_map_info);
    do_map_probe("map_rom",&xxx_map_info);

    匹配了设备驱动,可以发现一个map_info对象中没有mtd_partitions相关的信息,对于一个NOR Flash的分区信息,需要通过do_map_probe返回的mtd_info对象来注册到内核。这里我们可以先调用parse_mtd_partitions()查看Flash上已有的分区信息,获取了分区信息之后再调用add_mtd_partitions()将分区信息写入内核

    NOR Flash驱动模板

    #define WINDOW_SIZE ...
    #define WINDOW_ADDR ...
    static struct map_info xxx_map = {
        .name = "xxx flash",
        .size = WINDOW_SIZE,
        .bankwidth = 1,
        .phys = WINDOW_ADDR,
    };
    
    static struct mtd_partition xxx_partitions[] = {
        .name = "Drive A",
        .offset = 0,
        .size = 0x0e000,
    };
    
    #define NUM_PARTITIONS ARRAY_SIZE(xxx_partitions)
    
    static struct mtd_info *mymtd;
    
    static int __init init_xxx_map(void)
    {
        int rc = 0;
        xxx_map.virt = ioremap_nocache(xxx_map.phys, xxx_map.size);
        if(!xxx_map.virt){
            printk(KERN_ERR"Failed to ioremap_nocache\n");
            rc = -EIO;
            goto err2;
        }
        simple_map_init(&xxx_map);
        mymtd = do_map_probe("jedec_probe", &xxx_map);
        if(!mymtd){
            rc = -ENXIO;
            goto err1;
        }
        mymtd->owner = THIS_MODULE;
        add_mtd_partitions(mymtd, xxx_partitions, NUM_PARTITIONS);
        return 0;
    err1:
        map_destroy(mymtd);
        iounmap(xxx_map.virt);
    err2:
        return rc;
    }
    static void __exit cleanup_xxx_map(void)
    {
        if(mymtd){
            del_mtd_partitions(mymtd);
            map_destroy(mymtd);
        }
        
        if(xxx_map.virt){
            iounmap(xxx_map.virt);
            xxx_map.virt = NULL;
        }
    }

    Nand Flash驱动

    下图就是基于MTD框架的NandFlash驱动的位置。

    Nand Flash和NOR Flash类似,内核中已经在"drivers/mtd/nand/nand_base.c"中实现了通用的驱动程序,驱动开发中不需要再实现mtd_info中的read, write, read_oob, write_oob等接口,只需要构造并注册一个nand_chip对象, 这个对象主要描述了一片flash芯片的相关信息,包括地址信息,读写方法,ECC模式,硬件控制等一系列底层机制。当下,编写一个NandFlash驱动的工作流程如下:

    nand_chip

    这个结构描述一个NAND Flash设备,通常藏在mtd_info->priv中,以便在回调其中的接口的时候可以找到nand_chip对象。

    547 struct nand_chip {
    548         void __iomem *IO_ADDR_R;
    549         void __iomem *IO_ADDR_W;
    550 
    551         uint8_t (*read_byte)(struct mtd_info *mtd);
    578 
    579         int chip_delay;
    580         unsigned int options;
    581         unsigned int bbt_options;
    582 
    583         int page_shift;
    584         int phys_erase_shift;
    585         int bbt_erase_shift;
    586         int chip_shift;
    587         int numchips;
    588         uint64_t chipsize;
    589         int pagemask;
    590         int pagebuf;
    591         unsigned int pagebuf_bitflips;
    592         int subpagesize;
    593         uint8_t bits_per_cell;
    594         uint16_t ecc_strength_ds;
    595         uint16_t ecc_step_ds;
    596         int badblockpos;
    597         int badblockbits;
    598 
    599         int onfi_version;
    600         struct nand_onfi_params onfi_params;
    601 
    602         int read_retries;
    603 
    604         flstate_t state;
    605 
    606         uint8_t *oob_poi;
    607         struct nand_hw_control *controller;
    608 
    609         struct nand_ecc_ctrl ecc;
    610         struct nand_buffers *buffers;
    611         struct nand_hw_control hwcontrol;
    612 
    613         uint8_t *bbt;
    614         struct nand_bbt_descr *bbt_td;
    615         struct nand_bbt_descr *bbt_md;
    616 
    617         struct nand_bbt_descr *badblock_pattern;
    618 
    619         void *priv;
    620 };

    struct nand_chip
    --609-->NAND芯片的OOB分布和模式,如果不赋值,则会使用内核默认的OOB
    --580-->与具体的NAND 芯片相关的一些选项,如NAND_BUSWIDTH_16 等,可以参考<Linux/mtd/nand.h>
    --583-->用位表示的NAND 芯片的page 大小,如某片NAND 芯片的一个page 有512 个字节,那么page_shift 就是9 ;
    --584-->用位表示的NAND 芯片的每次可擦除的大小,如某片NAND 芯片每次可擦除16K 字节( 通常就是一个block 的大小) ,那么phys_erase_shift 就是14 ;
    --585-->用位表示的bad block table 的大小,通常一个bbt 占用一个block ,所以bbt_erase_shift 通常与phys_erase_shift 相等;
    --587-->表示系统中有多少片NAND 芯片;
    --588-->NAND 芯片的大小;
    --589-->计算page number 时的掩码,总是等于chipsize/page 大小 - 1 ;
    --590-->用来保存当前读取的NAND 芯片的page number ,这样一来,下次读取的数据若还是属于同一个page ,就不必再从NAND 芯片读取了,而是从data_buf 中直接得到;
    --596-->表示坏块信息保存在oob 中的第几个字节。对于绝大多数的NAND 芯片,若page size> 512,那么坏块信息从Byte 0 开始存储,否则就存储在Byte 5 ,即第六个字节。
    --619-->私有数据

    nand_scan()

    准备好了一个nand_chip,接下来的工作就是匹配驱动+注册分区表
    NAND flash使用nand_scan()来匹配驱动,这个函数会读取NAND芯片的ID,并根据mtd->priv即nand_chip中的成员初始化mtd_info。如果要分区,则以mtd_info和mtd_partition为参数调用add_mtd_partitions来添加分区信息。

    int nand_scan(struct mtd_info *mtd, int maxchips)  

    NAND Flash驱动模板

    #define CHIP_PHYSICAL_ADDRESS ...  
    #define NUM_PARTITIONS 2 
    static struct mtd_partition partition_info[] = { 
            { 
                    .name = "Flash partition 1", 
                    .info = 0, 
                    .size = 8 * 1024 * 1024, 
            }, 
            { 
                    .name = "Flash partition 2", 
                    offset = MTDPART_OFS_NEXT, 
                    size = MTDPART_SIZ_FULL, 
            }, 
    }; 
     
     
    int __init board_init(void) 
    { 
            struct nand_chip *this; 
            int err = 0; 
            /* 为MTD设备对象和nand_chip分配内存 */ 
            board_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip),GFP_KERNEL); 
            if(!board_mtd){ 
                    printk("Unable to allocate NAND MTD device structure\n"); 
                    err = -ENOMEM; 
                    goto out; 
            } 
     
            /* 初始化结构体 */ 
            memset((char *)board_mtd, 0 ,sizeof(struct mtd_info) + sizeof(struct nand_chip)); 
     
            /* 映射物理地址 */ 
            baseaddr = (unsigned long) ioremap(CHIP_PHYSICAL_ADDRESS,1024); 
            if(!baseaddr){ 
                    printk("Ioremap to access NAND Chip failed\n"); 
                    err = -EIO; 
                    goto out_mtd; 
            } 
     
            /* 获取私有数据(nand_chip)指针 */ 
            this = (struct nand_chip *)(&board_mtd[1]); 
     
            /* 将nand_chip赋予mtd_info私有指针 */ 
            board_mtd->priv = this; 
     
            /* 设置NAND Flash的IO基地址 */ 
            this->IO_ADDR_R = baseaddr; 
            this->IO_ADDR_W = baseaddr; 
     
            /* 硬件控制函数 */ 
            this->cmd_ctrl = board_hwcontrol; 
     
            /* 初始化设备ready函数 */ 
            this->dev_ready = board_dev_ready; 
             
            /* 扫描以确定设备的存在 */ 
            if(nand_scan(board_mtd, 1)){ 
                    err = -ENXIO; 
                    goto = out_ior; 
            } 
     
            /* 添加分区 */ 
            add_mtd_partitions(board_mtd,partition_info,NUM_PARTITIONS); 
            goto out; 
    out_ior: 
            iounmap((void *)baseaddr); 
    out_mtd: 
            kfree(board_mtd); 
    out: 
            return err; 
    } 
     
    static void __exit board_cleanup(void) 
    { 
            /* 释放资源,注销设备 */ 
            nand_release(board_mtd); 
     
            /* unmap物理地址 */ 
            iounmap((void *)baseaddr); 
     
            /* 释放MTD设备结构体 */ 
            kfree(board_mtd); 
    } 
     
    /* 硬件控制 */ 
    static void board_hwcontrol(struct mtd_info *mtd, int dat,unsigned int ctrl) 
    { 
            ... 
            if(ctrl & NAND_CTRL_CHANGE){ 
                    if(ctrl & NAND_NCE){ 
     
                    } 
            } 
            ... 
    } 
     
    /*返回ready状态*/ 
    static int board_dev_ready(struct mtd_info *mtd) 
    { 
            return xxx_read_ready_bit(); 
    } 

     

    展开全文
  • Linux MTD子系统学习(四) 5 Linux mtd设备注册 5.1 mtd字符设备注册 5.1.1 init_mtd 源码:drivers/mtd/mtdcore.c static int __init init_mtd(void) { int ret; ret = class_register(&mtd_class); if ...
  • MTD子系统归纳总结

    2015-09-15 08:37:05
    在Linux内核中引入了MTD子系统为NOR FLASH和NAND FLASH设备提供统一的接口,从而使得FLASH驱动的设计大为简化。 在引入MTD后Linux系统中FLASH设备驱动可分为四层: 1. 硬件驱动层:FLASH硬件驱动层负责FLASH硬件...
  • Linux MTD子系统学习(三)

    千次阅读 2019-06-22 09:55:41
    Linux MTD子系统学习(三) 4 Linux mtd分区的建立 4.1 mtd分区的建立方法 4.1.1 内核添加 在内核中添加分区表是就内核常用的方法,主要是在平台设备中添加mtd_parttion,如下: struct mtd_partition m25p80_part[]...
  • Linux MTD子系统学习(一)

    千次阅读 2019-06-22 09:33:55
    Linux MTD子系统学习(一) 1 Linux MTD基本概述 1.1 专有名词描述 1. MTD:Memory Technology Device,内存技术设备。 2. JEDEC:Joint Electron Device Engineering Council,电子电器设备联合会。 3. CFI:...
  • 基于块设备子系统的MTD子系统(2.6.26)

    千次阅读 2011-03-21 16:30:00
    基于块设备子系统的MTD子系统
  • Linux MTD子系统学习(二) 3 Linux MTD spi-nor驱动分析 3.1 spi-nor设备驱动框架 3.2 spi-nor设备注册 如果希望一个spi设备可以在linux系统下很好的工作,除了写驱动,还要向内核申明和注册这个spi设备。目前有两...
  • MTD子系统剖析

    2019-09-05 15:38:56
    概念 MTD:Memory Technology ... 2.MTD将文件系统与底层FLASH存储器进行了隔离 MTD分层图及对应作用: MTD设备通常可分为四层,从上到下依次是:设备节点、MTD设备层、MTD原始设备层、硬件驱动层 1.Flas...
  • 在上几篇文章中,基本上理清了mtd子系统的架构,并将mtd子系统对上的抽象及相应的函数接口进行了说明。从本章开始,主要以nandflash为例,介绍具体类型的闪存设备驱动模型是如何衔接mtd的抽象层,并完成对闪存芯片的...
  • 一、MTD框架 MTD设备通常可分为四层 上到下依次是:设备节点、MTD设备层、MTD原始设备层和硬件驱动层。 设备框架 二、MTD分区表的实现 在开机过程从console经常可以看到类似以下信息, 0x000000000000-...
  • MTD(Memory Technology Device)即常说的Flash等使用存储芯片的存储设备,MTD子系统对应的是块设备驱动框架中的设备驱动层,可以说,MTD就是针对Flash设备设计的标准化硬件驱动框架。本文基于3.14内核,讨论MTD驱动...
  • 【驱动】MTD子系统分析 MTD介绍  MTD,Memory Technology Device即内存技术设备  字符设备和块设备的区别在于前者只能被顺序读写,后者可以随机访问;同时,两者读写数据的基本单元不同。  字符设备...
  • Linux MTD子系统剖析

    2017-09-23 16:30:31
    MTD(memory technology device 存储 技术设备 ) 是用于访问 memory 设备( ROM 、 flash )的 Linux 的子系统MTD 的主要目的是为了使新的 memory 设备的驱动更加简单,为此它在硬件和上层之间提供了一个抽象的...
  • linux MTD子系统框架

    2020-03-26 09:11:44
    1. MTD:Memory Technology Device,内存技术设备 2. MTD分块设备和字符设备. 在/dev/目录下,可以看到类似mtd× / mtdblock× 文件 3. 打开 “/proc/mtd” 文件即可解析出MTD分区信息,从这里可以知道MTD数目,...
  • mtd子系统剖析-nandflash open/write/read分析图
  • Linux内核MTD子系统二之设备驱动模型

    千次阅读 2014-09-02 18:44:35
    经过UBOOT初步的移植,Linux内核初步的移植,Linux内核总线设备模型的分析,等一系列 ...一、MTD体系结构:Linux内核提供MTD子系统来建立FLASH针对Linux的统一、抽象接口。MTD将文件系统与底层的FLASH存储器进行
  • MTD子系统和NAND

    2018-03-31 11:31:34
    先前的文章《虚拟文件系统 (VFS)-基于linux3.10》和《UBIFS文件系统》只是对...NAND设备Linux将裸NAND(区别于emmc、usbstick)归纳到MTD设备类型里,这类设备通常相关的操作通常位于drivers/mtd/nand目录下。NAND...
  • linux之mtd子系统

    2018-05-08 10:02:40
    子系统架构二 关键数据结构三 数据流四 SOC驱动五 设备驱动

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 7,054
精华内容 2,821
关键字:

mtd子系统