S3C2440 Nor Flash驱动(二十四)

http://www.cnblogs.com/lifexy/p/7737174.html

1、Nor Flash硬件介绍:

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

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

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

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

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

比如:

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

当2440访问0x01地址时,由于2440的LDRR0线未接,所以还是访问Nor Flash的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命令如下所示(参考:MX29LV800BBTC.pdf和MX29LV160DBTI-70G.pdf)

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

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

2.1 比如,当我们要读ID操作:

NOR手册上:

往地址555H写AAH (发送解锁地址)

往地址2AAH写55H (发送解锁地址)

往地址555H写90H (发送命令)

读0地址得到厂家ID:C2H

读1地址得到设备ID:2249

退出读ID状态:给任意地址写F0H

(2440的A1接到NOR的A0,所以2440发出(555H<<1,左移1位相当于乘以2),NOR才能收到555H这个地址)

UBOOT的操作:

往地址AAAH写AAH    mw.w aaa aa 

往地址554H写55H    mw.w 554 55 

往地址AAAH写90H    mw.w aaa 90 

读0地址得到厂家ID:C2H    md.w 0 1 (1:表示读一次)

读1地址得到设备ID:2249    md.w 2 1

退出读ID状态:给任意地址写F0H    mw.w 0 f0

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

jedec

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

2.2.1 [0] = MTD_UADDR_0x0555_0x02AA

表示解锁地址为0x555,0x2AAAM,其中数组[0],表示属于8位flash

2.2.2 cmdset

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

2.2.3 .NumEraseRegions= 1

只有1个不同的扇区区域

2.2.4 ERASEINFO(0x10000,64)

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

cfi

就是将这些参数保存在cfi模式下指定地址中,往nor的0x55地址写入0x98即可进入cfi模式(从上图CFI Query可知道)

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

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

NOR手册:

进入CFI模式:往55H写入98H

读数据:读10H得到0051

读数据:读11H得到0052

读数据:读12H得到0059

nor的容量:读27H得到容量

退出CFI模式:复位

(2440的A1接到NOR的A0,所以2440发出(555H<<1,左移1位相当于乘以2),NOR才能收到555H这个地址)

UBOOT的操作:

进入CFI模式:往AAH写入98H    mw.w aa 98

读数据:读20H得到0051    md.w 20 1 (Q)

读数据:读22H得到0052    md.w 22 1 (R)

读数据:读24H得到0059    md.w 24 1 (Y)

nor的容量:读4EH得到容量    md.w 4e 1 (15)

退出CFI模式:复位    mw.w 0 f0

读到0x15,0x15的十进制是21,如下图,对应我们原理图的21根nor地址线,所以容量为2^21=2097152=2MB

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

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

2.4 写数据操作:(Program)

在地址0x100000(1M),写入0x1234
得到的还是原来的数据,所以不能像内存一样写

在地址0x30000000(内存),写入0x1234
得到的数据是0x1234

NOR手册:

往地址555H写AAH

往地址2AAH写55H

往地址555H写A0H

往地址PA写PD

(2440的A1接到NOR的A0,所以2440发出(555H<<1,左移1位相当于乘以2),NOR才能收到555H这个地址)

UBOOT的操作:

往地址AAAH写AAH    mw.w aaa aa

往地址554H写55H    mw.w 554 55

往地址AAAH写A0H    mw.w aaa a0

往地址0x100000写1234h    mw.w 100000 1234

读数据:读0x100000得到1234   md.w 100000 1

3、接下来便来分析如何写nor flash驱动

3.1 先来回忆一下之前的nand flash驱动:

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

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

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

进入它的init函数:

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

发现3个未定义的变量:

CONFIG_MTD_PHYSMAP_BANKWIDTH:nor flash的字节位宽

CONFIG_MTD_PHYSMAP_START:nor flash的物理基地址

CONFIG_MTD_PHYSMAP_LEN:nand flash的容量长度

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

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

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

 -> Device Drivers                                                   
          -> Memory Technology Device (MTD) support (MTD [=y])             
          -> Mapping drivers for chip access   //进入映射驱动


<M> CFI Flash device in physical memory map                      //支持cfi的nor flash设置为模块
  │ │    (0x0) Physical start address of flash mapping             //设置物理基地址  
  │ │    (0X1000000) Physical length of flash mapping         //设置容量长度,必须大于等于nor的2MB(0x1000000=16MB)
  │ │    (2)   Bank width in octets            //设置字节位宽,因为nor flash为16位,所以等于2(2字节*8位=16位)


3.5 make modules 编译模块

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

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

cp drivers/mtd/maps/physmap.ko  /work/nfsroot/first_fs

nfs 30000000 192.168.1.3:/work/nfsroot/uImage_nonand

bootm 30000000

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

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

其中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 + 1;       //容量长度
       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 0;

 

err_out:

       physmap_flash_remove(dev);                      //该函数用来注销之前注册的东西

       return err;

}

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

其中当*probe=="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来自己写nor flash驱动


/*
 * 参考: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;

//分区,在common-smdk.c里
static struct mtd_partition s3c_nor_parts[] = {
	[0] = {
        .name   = "bootloader_nor",//分区名
        .size   = 0x00040000,//分区大小
		.offset	= 0,//分区偏移值
	},
	[1] = {
        .name   = "root_nor",
        .offset = MTDPART_OFS_APPEND,//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),位宽(bank width),虚拟基地址(virt) */
	s3c_nor_map->name = "s3c_nor";//名字
	s3c_nor_map->phys = 0;//nor启动时,物理地址为0
	s3c_nor_map->size = 0x1000000;//0x1000000=16MB,要>=NOR的真正大小2MB
	s3c_nor_map->bankwidth = 2;//位宽16位
	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);//用某种规范识别nor flash
	if (!s3c_nor_mtd)//如果是空,没有识别出来,用另一种方法jedec
	{
		printk("use jedec_probe\n");
		s3c_nor_mtd = do_map_probe("jedec", 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);//来创建MTD字符/块设备
	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");


6、挂载驱动试验

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

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

可以看到创建了两个分区“bootloader”,"root",如下图所示,可以看到创建了两对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)

(一般,Nor Flash 格式化为jffs2,Nand Flash 格式化为yaffs)

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

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

猜你喜欢

转载自blog.csdn.net/xiaodingqq/article/details/81428656