1.nor硬件介绍:

从原理图中我们能看到NOR FLASH有地址线,有数据线,它和我们的SDRAM接口相似,能直接读取数据,但是不能像SDRAM直接写入数据,需要有命令才行

1.1其中我们2440的地址线共有27根(LADDR0~26),为什么是27根?

因为2440共有7个bank内存块,每个bank=128MB=(2^27)B,所以共有27根数据线

1.2为什么Nor Flash的地址线A0是接在2440的LADDR1上?

因为Nor Flash的数据共有16位,也就是每个地址保存了2B数据,而我们的2440每个地址是保存的1B数据,

比如:

当2440访问0X00地址时,就会读取到Nor上0地址的2B数据,然后2440的内存控制器会根据0x00来找到低8位字节,并返回给CPU,

当2440访问0x01地址时,由于2440的LADDR0线未接,所以还是访问Nor的0地址上的2B数据,然后内存控制器会根据0x01来找到高8位字节,并返回给CPU

1.3 nand和nor区别:

nor flash在价格上比nand贵,且容量很小 ,擦除和写数据都慢,好处在于接口简单,稳定,无位反转,坏块,常用于保存关键数据,而nand flash常用于保存大容量数据

在2440中是通过硬件开关来设置OM0为Nand启动还是Nor启动,如下图所示:

OM0具体参数如下所示,其中2440的OM1引脚默认接地

对于nand启动:OM0接地,nand flash的开始4KB会自动地被加载到2440内置的SRAM缓存器中,就可以直接读写

对于nor启动:OM0接高,2440访问的内存就是nor flash,可以直接读,但是不能直接写

2.nor flash命令如下所示(参考MX29LV160DBTI.pdf)

其中word是针对16位nand,byte针对8位nand.

由于我们2440的flash型号是MX29LV160DB,所以设备ID为0x2249

2.1 比如,当我们要program(往0x20地址写入0xff数据)时

需要以下3步:

1.发送解锁地址:

往nor地址0x555写入0xAA

往nor地址0x2AA写入0x55 

2.发送命令:     

往nor地址0x555写入0xA0                //进入program模式

3.写数据:

往nor地址0x20(PA)写入0xff(PD)          //往0x20写入0xff

(接下来就会一直是program模式,执行reset模式便可以退出)

2.2该NOR有两种规范, jedec, cfi(common flash interface) 

jedec

就是和nandflash的一样,通过读ID来匹配linux内核中drivers/mtd/chips/jedec_probe.c里的jedec_table[]数组,来确定norflash的各个参数(名称、容量、位宽等),如下图所示:

  • [0] = MTD_UADDR_0x5555_0x2AAA

表示解锁地址为0x5555,0x2AAAM,其中数组[0],表示属于8位flash,定义如下:

  •  CmdSet

使用哪种命令,一般CmdSet=0xFFF0

  • .NumEraseRegions= 1

只有1个不同的扇区区域

  • ERASEINFO(0x10000, 64)

共有64个扇区,每个扇区都是64KB(0x10000)

cfi

就是将这些参数保存在cfi模式下指定地址中, 往nor的0x55地址写入0x98,即可进入cfi模式,

cfi模式部分命令如下图所示:

当我们在cfi模式下,比如:读取nor地址0x27处的数据,便能读到nor的容量

如下图所示,之所以地址*2,是因为nor地址线A0接在我们2440的A1(退出cfi模式,使用复位命令即可)

读到0X15,0x15=21,如下图,刚好对应我们原理图的21根nor地址线,所以容量为2^21=2MB

2.3为什么上图的A20引脚没有接?

对于2440来讲,因为此时的A0~A19的容量刚好为2MB,与cfi模式下读取的数据一致,所以没有接A20

3.接下来便来分析如何写norflash驱动

3.1 先来回忆下之前的nandflsh驱动:

nandflsh驱动会放在内核的mtd设备中,而mtd设备知道如何通过命令/地址/数据来操作nandflash,所以我们之前的nandflash驱动只实现了硬件相关的操作(构造mtd_info,nand_chip结构体、启动nand控制器等)

同样地,norflash驱动也是放在内核的mtd设备中,mtd设备也知道对nor如何来读写擦除,只是不知道norflash的位宽(数据线个数),基地址等,所以我们的norflash驱动同样要实现硬件相关的操作,供给mtd设备调用

3.2参考内核自带的nor驱动:drivers/mtd/maps/physmap.c

进入它的init函数:

发现注册了两个platform平台设备驱动,进入physmap_flash结构体中:

发现3个未定义的变量:

CONFIG_MTD_PHYSMAP_BANKWIDTH: nandflash的字节位宽

CONFIG_MTD_PHYSMAP_START:nandflash的物理基地址

CONFIG_MTD_PHYSMAP_LEN: nandflash的容量长度

这3个变量是通过linux的menuconfig菜单配置出来的,若自己填入值,就不需要用menuconfig菜单配置了

 

3.3接下来我们就来配置内核,然后挂载这个内核自带的norflash驱动实验一番

3.4 首先make menuconfig,配置上面3个变量,然后设为模块

-> Device Drivers

-> Memory Technology Device (MTD) support

-> Mapping drivers for chip access                   //进入映射驱动


<M> CFI Flash device in physical memory map          //将支持cfi的norflash设置为模块

  • (0x0) Physical start address of flash mapping  // 设置物理基地址
  • (0x1000000) Physical length of flash mapping  // 设置容量长度,必须大于等于自身nor的2MB
  • (2)   Bank width in octets (NEW)                   // 设置字节位宽,因为nor为16位,所以等于2

3.5 make modules 编译模块

如下图所示,可以看到physmap.c编译成.ko模块了

3.6 然后放在nfs目录下,启动开发板

如下图所示,insmod后打印了一串信息:

如下图所示,可以看到创建了2个mtd0字符设备,一个mtd0块设备:

4.接下来我们便分析physmap.c,如何写出norflash驱动的

其中physmap.c的probe函数如下

struct physmap_flash_info {
struct mtd_info *mtd; //实现对flash的读写擦除等操作
struct map_info map; //存放硬件相关的结构体
struct resource *res;
#ifdef CONFIG_MTD_PARTITIONS
int nr_parts;
struct mtd_partition *parts;
#endif
}; static const char *rom_probe_types[] = { "cfi_probe", "jedec_probe", "map_rom", NULL }; //芯片名称

... ...
static int physmap_flash_probe(struct platform_device *dev)
{
const char **probe_type;
... ...
/*1. 分配结构体*/
info = kzalloc(sizeof(struct physmap_flash_info), GFP_KERNEL); /*2.设置map_info 结构体*/
    info->map.name = dev->dev.bus_id; //norflash的名字
info->map.phys = dev->resource->start; //物理基地址
info->map.size = dev->resource->end - dev->resource->start + ; //容量长度
info->map.bankwidth = physmap_data->width; //字节位宽
info->map.virt = ioremap(info->map.phys, info->map.size); //虚拟地址
simple_map_init(&info->map); //简单初始化map_info的其它成员 probe_type = rom_probe_types;
/*3. 设置mtd_info 结构体 */
/*通过probe_type指向的名称来识别芯片,当do_map_probe()函数返回NULL表示没找到*/
/*当找到对应的芯片mtd_info结构体,便返回给当前的info->mtd */
for (; info->mtd == NULL && *probe_type != NULL; probe_type++)
info->mtd = do_map_probe(*probe_type, &info->map); //通过do_map_probe ()来识别芯片 if (info->mtd == NULL) { //最终还是没找到芯片,便注销之前注册的东西并退出
dev_err(&dev->dev, "map_probe failed\n");
err = -ENXIO;
goto err_out;
} info->mtd->owner = THIS_MODULE; /*4.添加mtd设备*/ add_mtd_device(info->mtd); return ; err_out: physmap_flash_remove(dev); //该函数用来注销之前注册的东西 return err; }

通过上面的代码和注释分析到,和我们上一节的nandflash驱动相似,这里是设置map_info 结构体和mtd_info结构体来完成的,当我们要对norflash分区就要使用add_mtd_partitions()才行

其中当*probe_type==“cfi_probe”时:

就会通过do_map_probe("cfi_probe", &info->map)来识别芯片.

最终会进入drivers/mtd/chips/cfi_probe.c中的cfi_probe_chip()函数来进入cfi模式,读取芯片信息

当*probe_type=="jedec_probe"时:

最终会进入drivers/mtd/chips/jedec_probe.c中的jedec_probe_chip ()函数来使用读ID命令,通过ID来匹配jedec_table[]数组.

所以注册一个块设备驱动,需要以下步骤:

  • 1. 分配mtd_info结构体和map_info结构体
  • 2. 设置map_info 结构体
  • 3. 设置mtd_info 结构体
  • 4. 使用add_mtd_partitions()或者add_mtd_device()来创建MTD字符/块 设备

5.接下来我们来参考physmap.c来自己写norflah驱动

代码如下:

#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 mtd_info *mynor_mtd_info;
static struct map_info *mynor_map_info; static struct mtd_partition mynor_partitions[] = {
[] = {
.name = "bootloader",
.size = 0x00040000,
.offset = ,
},
[] = {
.name = "root",
.offset = MTDPART_OFS_APPEND,
.size = MTDPART_SIZ_FULL,
}
}; static const char *mynor_probe_types[] = { "cfi_probe", "jedec_probe",NULL}; static int mynor_init(void)
{
int val; /*1. 分配map_info 结构体和mtd_info结构体*/
mynor_mtd_info=kzalloc(sizeof(struct mtd_info), GFP_KERNEL);
mynor_map_info=kzalloc(sizeof(struct map_info), GFP_KERNEL); /*2. 设置map_info 结构体*/
mynor_map_info->name="my_nor";
mynor_map_info->phys=0x0; //物理地址
mynor_map_info->size=0x1000000; //=16M,长度必须大于等于norflash的2M容量
mynor_map_info->bankwidth=; //16位宽
mynor_map_info->virt = ioremap(0x0, mynor_map_info->size); //虚拟地址
simple_map_init(mynor_map_info); /*3. 设置mtd_info 结构体*/
   mynor_mtd_info = do_map_probe("cfi_probe", mynor_map_info);
   if (!mynor_mtd_info)
  {
 mynor_mtd_info = do_map_probe("jedec_probe", mynor_map_info);
 }

    if (!mynor_mtd_info)
    {
    printk("not available norflash !!!\r\n");
    goto err_out;
    }
    mynor_mtd_info->owner=THIS_MODULE;

/*4. 使用add_mtd_partitions()或者add_mtd_device()来创建MTD字符/块 设备*/
add_mtd_partitions(mynor_mtd_info,mynor_partitions,);
return ; err_out:
iounmap(mynor_map_info->virt); //取消虚拟地址映射
kfree(mynor_map_info);
kfree(mynor_mtd_info);
return ;
} static void mynor_exit(void)
{
del_mtd_partitions(mynor_mtd_info); //卸载分区
iounmap(mynor_map_info->virt); //取消虚拟地址映射
kfree(mynor_map_info);
kfree(mynor_mtd_info);
} module_init(mynor_init);
module_exit(mynor_exit);
MODULE_LICENSE("GPL");

6.挂载驱动试验

(一定要在nor启动下挂载才行,因为2440使用nand启动时,是访问不了nor的前4k地址)

insmod挂载驱动后,如下图所示:

可以看到创建了两个分区“bootloader”,“root”,如下图所示,可以看到创建了2对mtd字符/块设备

6.1 接下来便来对root分区(mtd1)来试验(使用flash之前最好擦除一次)

步骤如下:

./flash_eraseall -j /dev/mtd1                      //使用mtd-util工具的flash_eraseal命令来擦除root分区(mtd1)

mount -t jffs2 /dev/mtdblock1 /mnt/                //使用mount挂载文件系统, -t:文件系统类型(type)

接下来就可以在/mnt目录下来任意读写文件了,最终会保存在flash的mtdblock1块设备中

(PS:可以参考内核自带的mtdram.c,里面是使用内存来模拟flash, 里面通过memcopy()等来实现对内存读写擦除)

下章学习 : 26.Linux-网卡驱动介绍以及制作虚拟网卡驱动(详解)

25.Linux-Nor Flash驱动(详解)的更多相关文章

  1. 13.Linux键盘按键驱动 (详解)

    在上一节分析输入子系统内的intput_handler软件处理部分后,接下来我们开始写input_dev驱动 本节目标: 实现键盘驱动,让开发板的4个按键代表键盘中的L.S.空格键.回车键 1.先来介 ...

  2. Linux块设备驱动详解

    <机械硬盘> a:磁盘结构 -----传统的机械硬盘一般为3.5英寸硬盘,并由多个圆形蝶片组成,每个蝶片拥有独立的机械臂和磁头,每个堞片的圆形平面被划分了不同的同心圆,每一个同心圆称为一个 ...

  3. 很好的linux下GPIO驱动详解文章

    原文地址  http://blog.csdn.net/llxmedici/article/details/6282372 打算跟着友善之臂的<mini2440 linux移植开发指南>来做 ...

  4. 【转】草根老师的 linux字符设备驱动详解

    Linux 驱动 之 模块化编程 Linux 驱动之模块参数和符号导出 Linux 设备驱动之字符设备(一) Linux 设备驱动之字符设备(二) Linux 设备驱动之字符设备(三)

  5. linux usb 驱动详解

    linux usb 驱动详解 USB 设备驱动代码通过urb和所有的 USB 设备通讯.urb用 struct urb 结构描述(include/linux/usb.h ). urb 以一种异步的方式 ...

  6. Linux内核异常处理体系结构详解(一)【转】

    转自:http://www.techbulo.com/1841.html 2015年11月30日 ⁄ 基础知识 ⁄ 共 6653字 ⁄ 字号 小 中 大 ⁄ Linux内核异常处理体系结构详解(一)已 ...

  7. Linux下top命令详解

    Linux下top命令详解 top命令是Linux下常用的性能分析工具,能够实时显示系统中各个进程的资源占用状况,类似于Windows的任务管理器.top是一个动态显示过程,即可以通过用户按键来不断刷 ...

  8. 16.Linux-LCD驱动(详解)

    在上一节LCD层次分析中,得出写个LCD驱动入口函数,需要以下4步: 1) 分配一个fb_info结构体: framebuffer_alloc(); 2) 设置fb_info 3) 设置硬件相关的操作 ...

  9. (转)Linux 系统设置 : dmesg 命令详解

    原文:https://blog.csdn.net/yexiangCSDN/article/details/80683246 https://www.cnblogs.com/duanxz/p/34770 ...

随机推荐

  1. 201521123075 《Java程序设计》第13周学习总结

    1. 本周学习总结 协议 网络中为了进行数据交换(通信)而建立的规则.标准或约定(=语义+语法+规则),比如http, ftp等 IP层协议(Internet Protocol) Internet上的 ...

  2. 201521123121 《Java程序设计》第11周学习总结

    1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结多线程相关内容. 进程:每个进程都有独立的代码和数据空间,进程间的切换会有较大的开销,一个进程包含1--n个线程. 线程:同一类线程 ...

  3. Struts2第十一篇【简单UI标签、数据回显】

    Struts2UI标签 Sturts2为了简化我们的开发,也为我们提供了UI标签-也就是显示页面的标签-.. 但是呢,Struts2是服务端的框架,因此使用页面的标签是需要在服务器端解析然后再被浏览器 ...

  4. SQLite中Cursor类的说明

    在Android 查询数据是通过Cursor 类来实现的.当我们使用 SQLiteDatabase.query()方法时,就会得到Cursor对象, Cursor所指向的就是每一条数据. Cursor ...

  5. android动画的实现过程

    先上自己的测试代码,有参考apidemo中的AnimationDrawable中的方法 public class AnimateActivity extends Activity { @Overrid ...

  6. 利用原生js制做数据管理平台,适合初学者学习

    摘要:数据管理平台在当今社会中运用十分广泛,我们在应用过程中,要对数据进行存储,管理,以及删除查询等操作,而我们在实际设计的时候,大牛们大多用到的是JQuery,而小白对jq理解也较困难,为了让大家回 ...

  7. js'初学笔记

    之前看过一个博主说的学习前端养成写博客的习惯,我慢慢学着在上面写点东西,记录我的学习. 这段时间把之前学的js基础补上一点,学了一些对数组和字符的操作,split(),将字符串变成数组.join(), ...

  8. jenkins~集群分发功能的具体实现

    前一讲主要说了jenkins分发的好处<jenkins~集群分发功能和职责处理>,它可以让具体的节点干自己具体的事,比如windows环境下的节点,它只负责编译,发布windows的生态环 ...

  9. vue实例讲解之axios的使用

    本篇来讲解一下axios插件的使用,axios是用来做数据交互的插件. 这篇将基于vue实例讲解之vue-router的使用这个项目的源码进行拓展. axios的使用步骤: 1.安装axios npm ...

  10. BZOJ-1192-[HNOI2006]鬼谷子的钱袋

    Description 鬼谷子非常聪明,正因为这样,他非常繁忙,经常有各诸侯车的特派员前来向他咨询时政.有一天,他在咸阳游历的时候,朋友告诉他在咸阳最大的拍卖行(聚宝商行)将要举行一场拍卖会,其中有一 ...