• MTD下的Nand驱动



    title: MTD下的Nand驱动
    tags: linux
    date: 2018/12/26/ 17:07:22
    toc: true

    MTD下的Nand驱动

    引入

    我们从启动信息的打印入口

    S3C24XX NAND Driver, (c) 2004 Simtec Electronics
    s3c2440-nand s3c2440-nand: Tacls=3, 30ns Twrph0=7 70ns, Twrph1=3 30ns
    NAND device: Manufacturer ID: 0xec, Chip ID: 0xda (Samsung NAND 256MiB 3,3V 8-bit)
    Scanning device for bad blocks
    Bad eraseblock 609 at 0x04c20000
    Creating 4 MTD partitions on "NAND 256MiB 3,3V 8-bit":
    0x00000000-0x00040000 : "bootloader"
    0x00040000-0x00060000 : "params"
    0x00060000-0x00260000 : "kernel"
    0x00260000-0x10000000 : "root"
    

    搜索S3C24XX NAND Driver可以看到如下代码driversmtd ands3c2410.c,可以看到这个是platform平台设备驱动了,进入probe开始分析

    static int __init s3c2410_nand_init(void)
    {
    	printk("S3C24XX NAND Driver, (c) 2004 Simtec Electronics
    ");
    
    	platform_driver_register(&s3c2412_nand_driver);
    	platform_driver_register(&s3c2440_nand_driver);
    	return platform_driver_register(&s3c2410_nand_driver);
    }
    

    平台设备资源文件

    搜索名字可以找到平台的资源文件,包含了

    • 寄存器
    • 时序参数
    • 分区表
    设备平台的platform_device赋值
    struct platform_device s3c_device_nand = {
    	.name		  = "s3c2410-nand",
    	.id		  = -1,
    	.num_resources	  = ARRAY_SIZE(s3c_nand_resource),
    	.resource	  = s3c_nand_resource,		//分配了1M的寄存器地址
    };
    static struct resource s3c_nand_resource[] = {
    	[0] = {
    		.start = S3C2410_PA_NAND,
    		.end   = S3C2410_PA_NAND + S3C24XX_SZ_NAND - 1,
    		.flags = IORESOURCE_MEM,
    	}};
    s3c244x_map_io()
    	>s3c_device_nand.name = "s3c2440-nand";
    archarmplat-s3c24xxs3c244x.csmdk_machine_init
    	>s3c_device_nand.dev.platform_data = &smdk_nand_info;
    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,
    };
    
    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,
    	},
    };
    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,}};
    
    

    关键数据结构

    2440的程序使用s3c2410_nand_info包含了mtd驱动程序中必备的两个结构体

    • nand_chip 硬件操作层
    • mtd_info 协议层
    • s3c2410_nand_set 包含了分区表

    mark

    平台框架

    我们从probe入手开始分析

    mark

    s3c24xx_nand_probe

    驱动主要调用内核的nand_scan()函数,add_mtd_partitions()函数,来完成注册nandflash

    static int s3c24xx_nand_probe(struct platform_device *pdev, enum s3c_cpu_type cpu_type)
    {
    ... ...
    
    err = s3c2410_nand_inithw(info, pdev);       //初始化硬件hardware,设置TACLS 、TWRPH0、TWRPH1通信时序等
    
    s3c2410_nand_init_chip(info, nmtd, sets);    //初始化芯片
    
    nmtd->scan_res = nand_scan(&nmtd->mtd, (sets) ? sets->nr_chips : 1); //3.扫描nandflash
    ... ...
    s3c2410_nand_add_partition(info, nmtd, sets);         //4.调用add_mtd_partitions()来添加mtd分区
    ... ...
    }
    

    更详细的流程如下

    s3c24xx_nand_probe
    	// 获取平台设备  platform_device 的 dev.platform_data
    	struct s3c2410_platform_nand *plat = to_nand_plat(pdev);
    	
    	//设置平台设备中的 driver_data 具体的结构为 s3c2410_nand_info  设置为空
    	struct s3c2410_nand_info *info;
    	info = kmalloc(sizeof(*info), GFP_KERNEL);
    	memzero(info, sizeof(*info));
    	platform_set_drvdata(pdev, info);
    	
    	//设置info 中的时钟,并使能
    	info->clk = clk_get(&pdev->dev, "nand");
    	clk_enable(info->clk);
    	
    	//资源文件也就是寄存器空间
    	res  = pdev->resource;
    	size = res->end - res->start + 1;
    	info->area = request_mem_region(res->start, size, pdev->name);
    	
    	//设置info的具体结构数据
    	info->device     = &pdev->dev;
    	info->platform   = plat;
    	info->regs       = ioremap(res->start, size);
    	info->cpu_type   = cpu_type;
    	
    	//根据nand的时钟和tacls、twrph0、twrph1设置寄存器
    	err = s3c2410_nand_inithw(info, pdev);
    	
    	// 一个nr_set 中就有一张分区表,这里应该是指有几个分区表  我们这里就只有一个分区表
    	size = nr_sets * sizeof(*info->mtds);
    	info->mtds = kmalloc(size, GFP_KERNEL);
    	memzero(info->mtds, size);
    
    	nmtd = info->mtds;
    	
    	//设置chip中的一些函数指针 以及ecc 以及 info->sel_reg和info->sel_bit
    	s3c2410_nand_init_chip(info, nmtd, sets);
    
    	nmtd->scan_res = nand_scan(&nmtd->mtd, (sets) ? sets->nr_chips : 1);
    		//flash识别等
    		>nand_scan_ident(mtd, maxchips);
    			>nand_set_defaults 设置chip的读写函数等  nand_command、nand_select_chip。。。
    			//调用一些chip函数操作nand 读取id
    			>nand_get_flash_type
    				>chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);//NAND_CMD_READID=0x90
    					>在数组中nand_flash_ids 寻找具体信息
    						>{"NAND 256MiB 3,3V 8-bit",	0xDA, 0, 256, 0, LP_OPTIONS},
    					>获取页大小,擦出大小,位宽等
    					//打印flash信息
    					printk(KERN_INFO "NAND device: Manufacturer ID:"
    					   " 0x%02x, Chip ID: 0x%02x (%s %s)
    ", *maf_id,
    					   dev_id, nand_manuf_ids[maf_idx].name, mtd->name);
    		//获取mtd的一些信息,ecc等		
    		>nand_scan_tail(mtd);
    	
    	//sets 就是平台设备dev->platform_data->sets	
    	//根据分区表数目添加分区
    	s3c2410_nand_add_partition(info, nmtd, sets);
    		//nr_partitions 分区表的数目
    		if (set->nr_partitions > 0 && set->partitions != NULL)
    			add_mtd_partitions(&mtd->mtd, set->partitions, set->nr_partitions);
    				//nbparts=set->nr_partitions=分区表数目
    				for (i = 0; i < nbparts; i++)
    					
    					//创建一个mtd设备,一个分区表一个
    					add_mtd_device(&slave->mtd)
    						>	//找mtd_notifiers链表里的list_head结构体
    							list_for_each(this, &mtd_notifiers) {
    							struct mtd_notifier *not = list_entry(this, struct mtd_notifier, list);
    							// 通过list_head找到struct mtd_notifier *not,调用它的add函数
    							not->add(mtd);
    								//字符设备 创建字符设备文件
    								//块设备   调用gendisk 添加分区
    							}
    

    nand_scan

    • mtd_info 是协议相关
    • nand_chip 硬件相关

    这里主要用nand_chip提供的最基本的函数,来识别FALSH,再添加一些nand_chip中其他的函数以及参数,再调用nand_scan_tail添加mtd_info的成员

    nmtd->scan_res = nand_scan(&nmtd->mtd, (sets) ? sets->nr_chips : 1);
    	//flash识别等
    	>nand_scan_ident(mtd, maxchips);
    		>nand_set_defaults 设置chip的读写函数等  nand_command、nand_select_chip。。。
    		//调用一些chip函数操作nand 读取id
    		>nand_get_flash_type
    			>chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);//NAND_CMD_READID=0x90
    				>在数组中nand_flash_ids 寻找具体信息
    					>{"NAND 256MiB 3,3V 8-bit",	0xDA, 0, 256, 0, LP_OPTIONS},
    				>获取页大小,擦出大小,位宽等
    				//打印flash信息
    				printk(KERN_INFO "NAND device: Manufacturer ID:"
    				   " 0x%02x, Chip ID: 0x%02x (%s %s)
    ", *maf_id,
    				   dev_id, nand_manuf_ids[maf_idx].name, mtd->name);
    	//获取mtd的一些信息,ecc等		
    	>nand_scan_tail(mtd);
    

    s3c2410_nand_add_partition

    这里是根据分区表添加分区,调用系统提供的add_mtd_partitions来操作

    add_mtd_partitions

    这里会最终调用mtd_notifiers中的not->add(mtd)来添加分区表

    //创建一个mtd设备,一个分区表一个
    add_mtd_device(&slave->mtd)
    	>	//找mtd_notifiers链表里的list_head结构体
    		list_for_each(this, &mtd_notifiers) {
    		struct mtd_notifier *not = list_entry(this, struct mtd_notifier, list);
    		// 通过list_head找到struct mtd_notifier *not,调用它的add函数
    		not->add(mtd);
    

    那么如何去添加到分区表呢?这里是调用了 mtd_notifiers 中的 add函数成员,搜索全局变量mtd_notifiers来查看是在哪里初始化的,继续搜索register_mtd_user 可以看到有字符设备和块设备调用

    void register_mtd_user (struct mtd_notifier *new)
    {
            list_add(&new->list, &mtd_notifiers);
            for (i=0; i< MAX_MTD_DEVICES; i++)
                    if (mtd_table[i])
                            new->add(mtd_table[i]);
    }
    

    mark

    mark

    mark

    字符设备add

    会创建字符设备驱动mtd%d mtd%dro

    init_mtdchar
            >register_mtd_user
    #define MTD_CHAR_MAJOR 90
    #define MTD_BLOCK_MAJOR 31
    init_mtdchar	
    	register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops)
    	mtd_class = class_create(THIS_MODULE, "mtd");
    	register_mtd_user(&notifier);
    		static struct mtd_notifier notifier = {
    			.add	= mtd_notify_add,
    			.remove	= mtd_notify_remove,
    		};
    

    块设备add

    结论

    mtd_notifiers.add== blktrans_notify_add ->>>blktrans_majors.add_mtd == mtdblock_add_mtd

    在这里面会调用之前块设备驱动的gendisk的操作

    代码分析如下

    初始化在drivers/mtd/mtd_blkdevs.c中的register_mtd_blktrans

    int register_mtd_blktrans(struct mtd_blktrans_ops *tr)
    {
    	if (!blktrans_notifier.list.next)
    		register_mtd_user(&blktrans_notifier);
    }
    static struct mtd_notifier blktrans_notifier = {
    	.add = blktrans_notify_add,
    	.remove = blktrans_notify_remove,
    };
    

    也就是最终调用了blktrans_notify_add来添加分区表

    not->add == blktrans_notify_add

    这里blktrans_notify_add 又是通过一个链表来实现add的操作,也就是通过blktrans_majors中的add,继续搜索这个链表的初始化添加

    static void blktrans_notify_add(struct mtd_info *mtd)
    {
    	struct list_head *this;
    
    	list_for_each(this, &blktrans_majors) {
    		struct mtd_blktrans_ops *tr = list_entry(this, struct mtd_blktrans_ops, list);
    		tr->add_mtd(tr, mtd);
    	}
    
    }
    

    可以看到依然是在register_mtd_blktrans添加

    int register_mtd_blktrans(struct mtd_blktrans_ops *tr)
    {
    	  //这里初始化 mtd_notifiers 这个链表,这个链表的add会在添加分区表操作
    	if (!blktrans_notifier.list.next)
    		register_mtd_user(&blktrans_notifier);
    	
    	 // 这个是gendisk 的队列函数
    	  tr->blkcore_priv->rq = blk_init_queue(mtd_blktrans_request, &tr->blkcore_priv->queue_lock);
    	 tr->blkcore_priv->thread = kthread_run(mtd_blktrans_thread, tr,
    		"%sd", tr->name);
    	
    	//mtd_notifiers 中的add会调用blktrans_majors链表中的add_mtd,初始化blktrans_majors这个链表	
    	INIT_LIST_HEAD(&tr->devs);
    	list_add(&tr->list, &blktrans_majors);
    }
    

    搜索register_mtd_blktrans被mtdblock_ro.cmtdblock.c调用,一个只读,我们分析可读写的

    static int __init init_mtdblock(void)
    {
    	return register_mtd_blktrans(&mtdblock_tr);
    }
    

    也就是说最终的add指向如下

    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,
    };
    

    也就是说

    mtd_notifiers.add== blktrans_notify_add ->>>blktrans_majors.add_mtd == mtdblock_add_mtd

    在这里面会调用之前块设备驱动的gendisk的操作

    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)
    {
    	//分配一个gendisk
    	gd = alloc_disk(1 << tr->part_bits);
    	gd->major = tr->major;
    	gd->first_minor = (new->devnum) << tr->part_bits;
    	gd->fops = &mtd_blktrans_ops;
    	gd->private_data = new;
    	new->blkcore_priv = gd;
    	//设置队列
    	gd->queue = tr->blkcore_priv->rq;
    	//添加gendisk
    	add_disk(gd);
    }
    

    队列

    register_mtd_blktrans中有设置队列处理的函数

    // 这个是gendisk 的队列函数
    	  tr->blkcore_priv->rq = blk_init_queue(mtd_blktrans_request, &tr->blkcore_priv->queue_lock);
    	 tr->blkcore_priv->thread = kthread_run(mtd_blktrans_thread, tr,
    		"%sd", tr->name);
    
    

    处理函数如下,搜索blkcore_priv->thread 查看到mtd_blktrans_thread

    mtd_blktrans_request
    	 wake_up_process(tr->blkcore_priv->thread);
    
    static int mtd_blktrans_thread(void *arg)
    {
    	while (!kthread_should_stop()) {
    		//电梯调度算法
    		req = elv_next_request(rq);
    		
    		//操作
    		res = do_blktrans_request(tr, dev, req);
    		//一次获取结束
    		end_request(req, res);	
    }
    
    

    进入这个处理req的函数

    do_blktrans_request
    	case READ
    		tr->readsect
    	case WRITE
    		tr->writesect
    

    搜索发现就是刚才注册的mtdblock_tr

    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,
    };
    

    程序设计

    参考

    • 2440官方驱动driversmtd ands3c2410.c
    • 简单的driversmtd andat91_nand.c或者应该是通用的driversmtd andplat_nand.c

    平台设备文件

    可以看到,linux在这里使用platform平台总线来构造这个驱动,在2440中如下:

    • 寄存器
    • 时序参数
    • 分区表
    struct platform_device s3c_device_nand = {
    	.name		  = "s3c2410-nand",
    	.id		  = -1,
    	.num_resources	  = ARRAY_SIZE(s3c_nand_resource),
    	.resource	  = s3c_nand_resource,		//分配了1M的寄存器地址
    };
    static struct resource s3c_nand_resource[] = {
    	[0] = {
    		.start = S3C2410_PA_NAND,
    		.end   = S3C2410_PA_NAND + S3C24XX_SZ_NAND - 1,
    		.flags = IORESOURCE_MEM,
    	}};
    
    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,
    };
    
    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,
    	},
    };
    
    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,}};
    

    必备的构造结构

    查看下官方的driversmtd andplat_nand.c,可以看出来必备的结构

    struct plat_nand_data {
    	struct nand_chip	chip;
    	struct mtd_info		mtd;
    	void __iomem		*io_base;
    #ifdef CONFIG_MTD_PARTITIONS
    	int			nr_parts;
    	struct mtd_partition	*parts;
    #endif
    };
    
    • nand_chip 硬件操作相关
    • mtd_info 协议相关,在调用nand_scan_tail后会将里面的读写绑定到nand_chip 的读写
    xxx_nand_probe
    	>nand_scan
    		>nand_scan_ident //设置nand——chip
    			>nand_set_defaults
    			>nand_get_flash_type
    		>nand_scan_tail	 //设置mtd
    

    步骤简述

    设置nand_chip和mtd_info

    nand_chip提供了硬件操作的接口,这里必备的一些需要分析nand_scan来分析,nand_set_defaults里面有一些默认的函数,需要看下是否适用

    查看driversmtd andplat_nand.cprobe怎么设置的 mtd_infopriv需要指向nand_chip

    data->chip.priv = &data;
    
    //mtd必须的设置
    data->mtd.priv = &data->chip;
    data->mtd.owner = THIS_MODULE;
    //chip的一些设置
    data->chip.IO_ADDR_R = data->io_base;
    data->chip.IO_ADDR_W = data->io_base;
    data->chip.cmd_ctrl = pdata->ctrl.cmd_ctrl;
    data->chip.dev_ready = pdata->ctrl.dev_ready;
    data->chip.select_chip = pdata->ctrl.select_chip;
    data->chip.chip_delay = pdata->chip.chip_delay;
    data->chip.options |= pdata->chip.options;
    //ecc的设置,我们只需要设置 NAND_ECC_SOFT即可
    data->chip.ecc.hwctl = pdata->ctrl.hwcontrol;
    data->chip.ecc.layout = pdata->chip.ecclayout;
    data->chip.ecc.mode = NAND_ECC_SOFT;
    

    设置时钟

    /* 3. 硬件相关的设置: 根据NAND FLASH的手册设置时间参数 */
    /* 使能NAND FLASH控制器的时钟 */
    clk = clk_get(NULL, "nand");
    clk_enable(clk);              /* CLKCON'bit[4] */
    

    设置分区表

    函数原型如下

    int add_mtd_partitions(struct mtd_info *master,
    		       const struct mtd_partition *parts,
    		       int nbparts)
    //parts 为分区表
    //nbparts 表示这个分区表里面有几个分区
        
    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,
    	}
    };    
    

    测试

    内核配置去除NAND

    去除掉内核的nand驱动

    -> Device Drivers                                                                                                            │
    │       -> Memory Technology Device (MTD) support (MTD [=y])                                                                       │
    │         -> NAND Device Support (MTD_NAND [=y])  
    

    烧入内核后启动可以发现无法找到文件系统,疑问nand驱动已经被去除了

    Root-NFS: No NFS server available, giving up.
    VFS: Unable to mount root fs via NFS, trying floppy.
    VFS: Cannot open root device "mtdblock3" or unknown-block(2,0)
    Please append a correct "root=" boot option; here are the available partitions:
    Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(2,0)
    

    使用nfs文件系统启动

    设置uboot参数

    set bootargs noinitrd root=/dev/nfs nfsroot=172.16.45.222:/home/book/stu/fs ip=172.16.45.200:172.16.45.222:172.16.45.222:255.255.255.0::eth0:off  init=/linuxrc console=ttySAC0
    # 可以不save 也就是本次生效
    save
    
     mount -t nfs -o nolock,vers=2 172.16.45.222:/home/book/stu /mnt
    

    没有开启ECC的会提示ecc错误

    # insmod s3c_nand.ko
    NAND device: Manufacturer ID: 0xec, Chip ID: 0xda (Samsung NAND 256MiB 3,3V 8-bit)
    Scanning device for bad blocks
    Bad eraseblock 609 at 0x04c20000
    
    

    加载驱动

    # mkdir nfs_stu
    # mount -t nfs -o nolock,vers=2 172.16.45.222:/home/book/stu /nfs_stu/
    # cd /nfs_stu/code/
    # insmod  s3c_nand.ko
    

    分区表打印

    挂载驱动后可以打印分区表了

    # insmod s3c_nand.ko
    NAND device: Manufacturer ID: 0xec, Chip ID: 0xda (Samsung NAND 256MiB 3,3V 8-bit)
    Scanning device for bad blocks
    Bad eraseblock 609 at 0x04c20000
    Creating 4 MTD partitions on "NAND 256MiB 3,3V 8-bit":
    0x00000000-0x00040000 : "bootloader"
    0x00040000-0x00060000 : "params"
    0x00060000-0x00260000 : "kernel"
    0x00260000-0x10000000 : "root"
    
    
    # ls /dev/mtd* -l
    crw-rw----    1 0        0         90,   0 Jan  1 00:06 /dev/mtd0
    crw-rw----    1 0        0         90,   1 Jan  1 00:06 /dev/mtd0ro
    crw-rw----    1 0        0         90,   2 Jan  1 00:06 /dev/mtd1
    crw-rw----    1 0        0         90,   3 Jan  1 00:06 /dev/mtd1ro
    crw-rw----    1 0        0         90,   4 Jan  1 00:06 /dev/mtd2
    crw-rw----    1 0        0         90,   5 Jan  1 00:06 /dev/mtd2ro
    crw-rw----    1 0        0         90,   6 Jan  1 00:06 /dev/mtd3
    crw-rw----    1 0        0         90,   7 Jan  1 00:06 /dev/mtd3ro
    brw-rw----    1 0        0         31,   0 Jan  1 00:06 /dev/mtdblock0
    brw-rw----    1 0        0         31,   1 Jan  1 00:06 /dev/mtdblock1
    brw-rw----    1 0        0         31,   2 Jan  1 00:06 /dev/mtdblock2
    brw-rw----    1 0        0         31,   3 Jan  1 00:06 /dev/mtdblock3
    
    
    

    查看下分区表的信息

    # cat /proc/partitions
    # 其中blocks表示分区的容量,每个blocks是1KB
    major minor  #blocks  name
      31     0        256 mtdblock0
      31     1        128 mtdblock1
      31     2       2048 mtdblock2
      31     3     259712 mtdblock3
    

    挂载这个分区

    mount /dev/mtdblock3        /mnt/
    

    mtd-utils

    编译安装,这里util依赖zlib,还要安装这个,zlib要安装到交叉编译器中,使用which查看

    # tar xzvf zlib-1.2.3.tar.gz
    # which arm-linux-gcc
    /opt/gcc-3.4.5-glibc-2.3.6/bin/arm-linux-gcc
    #其中-prefix指定zlib的安装路径,需要指定到交叉编译器所在路径
    #CC=arm-linux-gcc ./configure --shared --prefix=/opt/gcc-3.4.5-glibc-2.3.6/arm-linux
    #make
    #make install
    
    #------------------------------------------------------------------
    #mkdir tmp
    #tar xjf mtd-utils-05.07.23.tar.bz2  -C tmp/
    #cd tmp
    #cd util/
    #make
    

    复制到nfs的文件系统提供给单板使用

    cp  flash_erase  flash_eraseall flashcp  /stu/fs
    

    使用工具格式化后挂载

    FAQ : 格式化使用字符设备

    因为每个分区的字符设备,其实就是对应着每个分区块设备。即/dev/mtd3对应/dev/mtdblock3

    flash_eraseall, flash_erase那些命令是以ioctl等基础而实现, 而块设备不支持ioctl, 只有字符设备支持

    #擦除分区3,也就是文件系统的分区
    ./flash_eraseall /dev/mtd3
    
    #挂载刚才格式化 的分区3 也就是 root
    # mount -t yaffs /dev/mtdblock3 /mnt
    yaffs: dev is 32505859 name is "mtdblock3"
    yaffs: passed flags ""
    yaffs: Attempting MTD mount on 31.3, "mtdblock3"
    yaffs: auto selecting yaffs2
    block 591 is bad
    
    # ls /mnt
    lost+found
    
    #卸载
    umount /mnt
    

    复制文件系统回来

    卸载这个mnt可能发现无法卸载,查看使用的pid后杀掉,发现是sh占用了

    umount: cannot umount /mnt: Device or resource busy
    fuser  /mnt
    kill -9 pid
    

    挂载后复制文件,这个/nfs_stu/fs/*是在nfs上的文件系统

    cp /nfs_stu/fs/*  /mnt -r 
    # 提示一下可以忽略,因为nand驱动不全 https://blog.csdn.net/grublyboy/article/details/9664169
    yaffs chunk 773 was not erased
    

    重启系统,设置启动参数,设置内核加载原来的NAND的驱动

    set bootargs noinitrd root=/dev/mtdblock3 init=/linuxrc console=ttySAC0 user_debug=0xff
    

    系统可以正常启动

    完整程序

    
    /* 参考 
     * driversmtd
    ands3c2410.c
     * driversmtd
    andat91_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 */
    	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");
    
    
    
  • 相关阅读:
    假设法求最大值和数组的优点
    要明白每个变量的语义,尽量避免一个变量有多重语义
    掷色子6000次分别统计出点子出现的次数
    c语言函数是怎么传递参数的
    为什么rand和srand总是同时出现?
    c语言解二元二次方程组
    【译】第三篇 Replication:事务复制-发布服务器
    【译】第二篇 Replication:分发服务器的作用
    【译】第一篇 Replication:复制简介
    【译】第八篇 Integration Services:高级工作流管理
  • 原文地址:https://www.cnblogs.com/zongzi10010/p/10178914.html
Copyright © 2020-2023  润新知