Zynq-Linux移植学习笔记之18-Zynq下NOR_FLASH挂载文件系统

1、  背景介绍

板子上的zynq通过emc外接一块nor flash,地址分配如下:


Nor flash的起始地址为0x80000000。当zynq上运行linux后可以通过对该地址起始的区域进行擦除、读写操作从而对NOR FLASH进行操作。具体参看前一篇博客点击打开链接

不过,这种实现方式虽然简单,但对用户来说使用不方便,没有用户会自己计算需要擦除多少扇区或读写多少扇区的。基于这点考虑,在nor flash上面挂载文件系统就显得格外重要。

 

2、  MTD介绍

在linux中挂载文件系统需要借助MTD系统,结构图如下:


具体每层的含义以及每层之间是如何交互的可以参看宋宝华的《Linux驱动开发详解》这本书。

对于NOR FLASH来说,MTD往下的层次结构如下:


上图中提到了需要使用CFI JFDEC等驱动,这些驱动已经实现了大多数常见的NOR FLASH操作,驱动开发人员完全不需要实现最底层的FLASH操作序列。

为了使用这些驱动,加载NOR FLASH时需要进行下面操作:


根据板子上的NOR FLASH编写对应的代码,代码中需要做的事情不多。

(1)       定义 map_info 的实例,初始化其中的成员,根据目标板的情况为 name、size、bankwidth 和 phys 赋值。

(2)      如果 Flash 要分区,则定义 mtd_partition 数组,将实际电路板中 Flash 分区信息记录于其中。

(3)      以 map_info 和探测的接口类型(如"cfi_probe"、"jedec_probe"等)为参数调用do_map_ probe(),探测Flash 得到mtd_info。do_map_probe()会根据传入的参数 name 通过 get_mtd_chip_driver()得到具体的MTD驱动,调用与接口对应的 probe()函数探测设备,利用 map_info 中的配置,do_map_probe()可以自动识别支持 CFI 或 JEDEC(电子元件工业联合会)接口的 Flash 芯片,MTD以后会自动采用适当的命令参数对 Flash进行读写或擦除。

(4)       在模块初始化时以 mtd_info 为参数调用 add_mtd_device()或以mtd_info、mtd_partition数组及分区数为参数调用 add_mtd_partitions()注册设备或分区。当然,在这之前可以调用 parse_mtd_partitions()查看Flash 上是否已有分区信息,并将查看出的分区信息通过 add_mtd_partitions()注册。

最后将代码编译到内核中即可。

 

3、 代码实现

下面是根据板子的具体配置编写的NOR FLASH驱动代码,其实也就是执行上面的几个操作,配置一下相关的信息。

 

#include <linux/module.h>
#include <linux/slab.h>
#include <linux/ioport.h>
#include <linux/vmalloc.h>
#include <linux/init.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/mtdram.h>
#include <linux/io.h>
#include <linux/pci.h>
#include <linux/kernel.h>
#include <asm/io.h>
#include <linux/mtd/map.h>
#include <linux/mtd/partitions.h>
#include <linux/of_platform.h>
#include "mtdcore.h"

#define WINDOW_ADDR 0x80000000 
#define WINDOW_SIZE 0x8000000 
#define BUSWIDTH 2

#define PROBETYPES { "cfi_probe", NULL }

#define MSG_PREFIX "S3C2410-NOR:" 
#define MTDID "s3c2410-nor" 
#define CONFIG_MTD_PARTITIONS

static struct mtd_info *mymtd;
 
struct map_info s3c2410nor_map = // map_info
{
 .name = "NOR Flash on S3C2410",
 .size = WINDOW_SIZE,
 .bankwidth = BUSWIDTH,
 .phys = WINDOW_ADDR,
};
 
 
 #ifdef CONFIG_MTD_PARTITIONS

static struct mtd_partition static_partitions[] =
{
	{
	.name = "test1", .size = 0x200000, .offset = 0x020000
	} ,
	{
	.name = "test2", .size = 0x400000, .offset = 0x200000
	} ,
};
#endif
 
 
static int mtd_parts_nb = 0;
static struct mtd_partition *mtd_parts = 0;
 
int __init init_s3c2410nor(void)
{
	 static const char *rom_probe_types[] = PROBETYPES;
	 const char **type;
	 const char *part_type = 0;
	 
	 printk(KERN_NOTICE MSG_PREFIX "0x%08x at 0x%08x\n", WINDOW_SIZE,WINDOW_ADDR);
	 s3c2410nor_map.virt = ioremap(WINDOW_ADDR, WINDOW_SIZE);
	 if (!s3c2410nor_map.virt)
	 {
		 printk(MSG_PREFIX "failed to ioremap\n");
		 return - EIO;
	 }
	 simple_map_init(&s3c2410nor_map);
	 mymtd = 0;
	 type = rom_probe_types;
	 for (; !mymtd && *type; type++)
	 {
	 		mymtd = do_map_probe(*type, &s3c2410nor_map);
	 }
	 if (mymtd)
	 {
	 	 mymtd->owner = THIS_MODULE;
		 #ifdef CONFIG_MTD_PARTITIONS
		
		 if (mtd_parts_nb == 0) //using default partitions
		 {
		 	mtd_parts = static_partitions;
		 	mtd_parts_nb = ARRAY_SIZE(static_partitions);
		 	part_type = "static";
		 }
		 
		 #endif
	 
		 add_mtd_device(mymtd);
		 printk("mtd parts is %d\n",mtd_parts_nb);
		 
		 if (mtd_parts_nb == 0)
		 {
			printk(KERN_NOTICE MSG_PREFIX "no partition infoavailable\n");
		 }
		 else
		 {
		 	printk(KERN_NOTICE MSG_PREFIX "using %s partitiondefinition\n",part_type);
			add_mtd_partitions(mymtd, mtd_parts, mtd_parts_nb);
		 }
		 return 0;
	 }
 	 iounmap((void*)s3c2410nor_map.virt);
 	 return - ENXIO;
}

static void __exit cleanup_s3c2410nor(void)
{
 if (mymtd)
 {
	 del_mtd_partitions(mymtd); 
	 del_mtd_device(mymtd); 
	 map_destroy(mymtd); 
 }
 if (s3c2410nor_map.virt)
 {
	 iounmap((void*)s3c2410nor_map.virt);
	 s3c2410nor_map.virt = 0;
 }
}

module_init(init_s3c2410nor);
module_exit(cleanup_s3c2410nor);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Alexander Larsson <alexl@redhat.com>");
MODULE_DESCRIPTION("Simulated MTD driver for testing");


 

需要配置的是

#defineWINDOW_ADDR 0x80000000 //和vivado里面的emc起始地址一致

#defineWINDOW_SIZE 0x8000000 //nor flash大小

#defineBUSWIDTH 2 //这里需要注意,在vivado中配置的宽度为16,见下图,但这里填写的是字节宽度,所以为2


代码中选择手动配置好分区信息,就不需要从devicetree配置中去读了,再说也不知道把NOR FLASH的分区信息放在devicetree中的何处。上面代码中建了2个分区

将代码放在drivers/mtd/chips下


makefile中暴力加入:

obj-y                            +=S3C2410.o


就可以将模块编译进内核中了。

 

4、 测试

在linux启动过程中能看到下面信息:


可以看到这里打印出了NOR FLASH的起始地址(这里是物理地址,虚实映射在init函数中做了),两个分区信息,分区情况和代码中写的一致。

可以通过cat/proc/mtd查看系统内所有的分区信息


Mtd0为NORFLASH的全部区域,大小为0x8000000,和代码中的WINDOW_SIZE一样,test1,test2自然也在这里。这里需要注意的是,自己建立的分区大小和偏移量需要为erasesize的整数倍,因为MTD是按块进行擦除的,否则会出现以下错误:



接下来可以进行文件读写测试,如图


上面对test1进行了擦除,挂载文件系统,写入文件,重新上电后查看文件信息


可以看到文件依然存在,测试通过。

 

5、 总结

由于被同事忽悠,之前以为挂载文件系统是一件很复杂的事情,现在发现linux中现有的驱动已经帮做了很多事情,我们需要做的只是简单的配置,正如宋宝华在《Linux设备驱动开发详解》这本书中所说,由于引入了 MTD 系统以及 MTD 下层的通用 NOR 和NAND 驱动,Linux 中 NOR和NAND Flash 芯片级驱动的设计难度甚至要低于一个普通的 GPIO 字符设备。

  • 2
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值