总览
本文使用 linux-2.6.22.6 内核, 使用jz2440开发板.
NOR Flash 基础知识
NAND 和 NOR Flash的比较
NOR FLASH |
NAND FLASH |
接口时序同SRAM,易使用 |
地址/数据线复用,数据位较窄 |
读取速度较快 |
读取速度较慢 |
擦除速度慢,以64-128KB的块为单位 |
擦除速度快,以8-32KB的块为单位 |
写入速度慢 |
写入速度快 |
随机存取速度较快,支持XIP(eXecute In Place,芯片内执行),适用于代码存储。在嵌入式系统中,常用于存放引导程序、根文件系统等。 |
顺序读取速度较快,随机存取速度慢,适用于数据存储(如大容量的多媒体应用)。在嵌入式系统中,常用于存放用户文件系统等。 |
单片容量较小,1-32MB |
单片容量较大,8-128MB,提高了单元密度 |
最大擦写次数10万次 |
最大擦写次数100万次 |
硬件接口
看相关数据手册, 以jz2440v3开发板为例:
MX29LV160DBTI-70G.pdf
NOR Flash 数据手册
S3C2440A_UserManual_Rev13.pdf
CPU 数据手册
注意几点:
- NOR Flash 的特性和RAM一样, 可以直接用物理地址来操作.
- 当开发板以NOR Flash启动时, 0开始的地址就是指向NOR Flash的.
- NOR Flash 数据位宽有两种接法, 16bit 和 8bit. jz2440用的16bit接法
- 因此, 用uboot测试时, 需要使用
mw.w
md.w
来操作内存地址
mw
Memory Write. uboot下的写内存指令
md
Memory Display. uboot下的读内存指令
- 使用16bit接法时, CPU的地址线0是不接的. 因而指令上有个错位.
- 譬如: jz2440发出 (555h<<1), NOR Flash才能收到555h这个地址.
- NOR Flash 有两种模式, jedec, cfi
- jedec, 无法直接从芯片内读取详细信息, 需要根据芯片ID软件查表.
- cfi, Common Flash Interface, 可以直接查询芯片详细信息.
- 目前大多数 NOR Flash 都支持 cfi 规范.
读写实验
mw.w aaa aa mw.w 554 55 mw.w aaa 90 md.w 0 1
md.w 2 1
mw.w 0 f0
|
NOR Flash 系统框架
系统自带驱动
$ make clean $ make menuconfig
$ make modules cp ./drivers/mtd/maps/physmap.ko ~/share/jz2440/drivers/nor/
$ ls /dev/mtd* $ insmod physmap.ko $ ls /dev/mtd* $ cat /proc/mtd
|
框架分析
其基本框架和 NAND Flash 是一样的
下面, 分析一下 CFI NOR Flash 的内核代码框架
module_init(physmap_init) platform_driver_register(&physmap_flash_driver); platform_device_register(&physmap_flash);
# 匹配后, 自然是调用probe函数 physmap_flash_probe probe_type = rom_probe_types; do_map_probe(*probe_type, &info->map);
do_map_probe drv = get_mtd_chip_driver(name); list_entry(pos, typeof(*this), list) drv->probe(map);
add_mtd_device
static struct mtd_chip_driver cfi_chipdrv; cfi_probe mtd_do_chip_probe(map, &cfi_chip_probe);
mtd_do_chip_probe genprobe_ident_chips cp->probe_chip check_cmd_set
cfi_probe_chip cfi_send_gen_cmd
|
这样就比较清楚了, 整个Linux代码尽可能的做到功能上的(代码上没有完全做到)分层分离.
大框架下有小框架. 譬如 NOR Flash 属于整个MTD大框架的一部分. 但其内部也有自己的一套小框架.
在 NOR Flash 这个例子里面,
将通用的底层驱动代码放在文件 /drivers/mtd/maps/physmap.c
然后probe时, 具体的硬件操作被拆分三个部分 cfi_probe.c
jedec_probe.c
map_rom.c
由于probe里面也有共性的东西, 又被提炼成 gen_probe.c
放在一起.
最后, 看一个最简单的例子, ram模拟mtd设备. 将底层硬件相关操作减到了最少.
这个RAM mtd设备和 NAND/NOR Flash 平级, 挂在 mtdcore.c
下.
module_param_call(phram, phram_setup, NULL, NULL, 000); phram_setup register_device(name, start, len); new->mtd.XXX = XXX; add_mtd_device
add_mtd_device not = list_entry(this, struct mtd_notifier, list); not->add(mtd);
static struct mtd_notifier notifier; mtd_notify_add class_device_create("mtd%d") class_device_create("mtd%dro") init_mtdchar register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops)
static struct mtd_notifier blktrans_notifier; blktrans_notify_add tr = list_entry(this, struct mtd_blktrans_ops, list); tr->add_mtd(tr, mtd);
static struct mtd_blktrans_ops mtdblock_tr; mtdblock_add_mtd add_mtd_blktrans_dev alloc_disk add_disk init_mtdblock register_mtd_blktrans register_blkdev blk_init_queue
|
源码
s3c_nor.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>
MODULE_LICENSE("GPL"); MODULE_AUTHOR("DRAAPHO");
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) { s3c_nor_map = kzalloc(sizeof(struct map_info), GFP_KERNEL);;
s3c_nor_map->name = "s3c_nor"; s3c_nor_map->phys = 0; s3c_nor_map->size = 0x2000000; s3c_nor_map->bankwidth = 2; s3c_nor_map->virt = ioremap(s3c_nor_map->phys, s3c_nor_map->size); simple_map_init(s3c_nor_map);
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; }
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);
|
Makefile
obj-m := s3c_nor.o KERN_SRC := /home/draapho/share/jz2440/kernel/linux-2.6.22.6/ PWD := $(shell pwd)
modules: make -C $(KERN_SRC) M=$(PWD) modules
clean: make -C $(KERN_SRC) M=$(PWD) clean
|
测试
$ make modules
$ ls /dev/mtd* $ insmod s3c_nor.ko $ ls /dev/mtd* $ flash_eraseall -j /dev/mtd5 $ mount -t jffs2 /dev/mtdblock5 /mnt
$ tar xjf mtd-utils-05.07.23.tar.bz2 $ cd mtd-utils-05.07.23/util $ vim Makefile CROSS=arm-linux- $ make
$ cp flash_erase flash_eraseall /bin
|
原创于 DRA&PHO