这是一篇实现linux内核对nor flash的支持的一点资料,很早以前写的,先整理如下:
nor flash芯片:JS28F128J3D -75-128Mb
网上有文章说了如何让linux内核支持nor flash。不过那些转载的文章中没有头文件(因为使用了<尖括号>,在HTML语言中是注释的意思)。后来研究了类似的驱动文件,发现它们都是大同小异,只是在一些参数上有改变而已。
MTD设备驱动程序在./driver/mtd下面,分为nor flash和nand flash两种。在chips中定义了几种访问nor的接口,包括cfi、jedec、map_ram和map_rom,而实际芯片所要添加的“驱动程序”放到maps目录下(比如本文使用的at91rm9200.c文件,详见后面)。从map这个单词来看,它描述的是一些映射信息,当然也包括了芯片信息,如总大小、块总数、访问接口等等。
这个过程其实是在内核代码树中添加自己的驱动的过程,当然是一些“标准”步骤了:在对应的代码树目录下,添加驱动源代码文件、在Makefile添加内容、在Kconfig添加内容,当然,还需要在make menuconfig里配置。
1、./driver/mtd/maps/Makefile
在这个Makefile最后添加:
obj-$(CONFIG_MTD_AT91RM9200) +=at91rm9200.o
这个CONFIG_MTD_ AT91RM9200由内核配置所得。
2、./driver/mtd/maps/Kconfig
在这个Kconfig文件最后添加(但在endmenu之前):
config MTD_ AT91RM9200
tristate "CFI Flash device mapped on AT91RM9200"
depends on ARM && MTD_CFI
help
This enables access to the CFI Flash on the SMDK2440 board.
If you have such a board, say 'Y' here.
这里的MTD_AT91RM9200就是前面Makefile中的那个,注意,CONFIG_前缀是内核自己添加的。可以在编译内核后生成的autoconf.h文件中查看该宏定义。
3、内核配置
首先进入drivers/mtd/maps/目录下,编写flash芯片的map文件,例如文件名为at91rm9200.c。在map文件中需要定义flash的基地址、大小、宽度、分区表、读写函数、初始化和释放函数等。其中分区表的一个例子如下:
static struct mtd_partition at91rm9200_partitions[]=
{
{
name:"u-boot",
size:0x00040000,
offer:0x0,
mask_flags:mtd_writeable,
},
{
name:"kernel",
size:0x00200000,
offset: 0x00040000,
},
{
name:"JFFS2",
size:mtdpart_siz_full, //将所有剩余空间都分配给文件系统
offset: 0x00260000,
}
};
这里将flash分成3个分区,分别存放引导装载程序(bootloader)、内核和根文件系统。mask_flags:mtd_writeabie表示分区是只读的;mtdpart_ofs_append或0x00040000表示分区位置紧挨着上一个分区;mtdpart_siz_full表示占用剩余的全部空间。文中将jffs2文件系统的映像存放在第3个分区上,作为系统的根文件系统。
然后修改makefile文件,在其中加入以下语句:
obj -$(config_mid_ at91rm9200) + = at91rm9200.o
然后回到linux源码目录下,运行make menuconfig对操作系统内核进行配置与裁减,要在nor flash上使用jffs2文件系统,要特别注意以下两个选项:
1)在“memory technology devices(mtd)”选项中选择:
<*>memory technology device(mtd)support
<*>mtd partitioning support
<*>cashing block device access to mtd devices
ram/rom/flash chip drivers--->
<*> detect flash chips by common flash interface(cfi) probe
<*> support for intel/sharp flash chips
mapping drivers for chip access--->
<*> cfi flash device in physical memory map
(0) physical start address of flash mapping
(1000000)physical length of flash mapping
(2)buswidthin octets
<*> intel strataflash device mapped on at91rm9200
这里设置了flash芯片的起始地址、大小以及宽度,比如:起始地址为“0”,大小为“1000000”,即16mb,宽度为“2”,即16字节。然后选中自己加入的“intel strata flash device mapped on EDB7312”这一项。
2)在“file systems”选项中选择:
<*>joumalling flash file system v2(jffs2)support
对于其他功能,可根据自己的实际情况,进行裁减,然后保存退出,接下来编译出操作系统内核映像。
4、./driver/mtd/maps/at91rm9200.c
/*
* Handle mapping of the NOR flash on Cogent EDB7312 boards
*
* Copyright 2002 SYSGO Real-Time Solutions GmbH
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <asm/io.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
#ifdef CONFIG_MTD_PARTITIONS
#include <linux/mtd/partitions.h>
#endif
#define WINDOW_ADDR 0x10000000 /* physical properties of flash */
#define WINDOW_SIZE 0x01000000
#define BUSWIDTH 2
#define FLASH_BLOCKSIZE_MAIN 0x20000
#define FLASH_NUMBLOCKS_MAIN 128
/* can be "cfi_probe", "jedec_probe", "map_rom", NULL }; */
#define PROBETYPES { "cfi_probe", NULL }
#define MSG_PREFIX "AT91RM9200-NOR:" /* prefix for our printk()'s */
#define MTDID "at91rm9200-%d" /* for mtdparts= partitioning */
static struct mtd_info *mymtd;
struct map_info at91rm9200nor_map = {
.name = "NOR flash on AT91RM9200DK",
.size = WINDOW_SIZE,
.bankwidth = BUSWIDTH,
.phys = WINDOW_ADDR,
};
#ifdef CONFIG_MTD_PARTITIONS
/*
* MTD partitioning stuff
*/
static struct mtd_partition at91rm9200nor_partitions[] =
{
{
name: "U-boot",
size: 0x60000,
offset: 0x0,
mask_flags: MTD_WRITEABLE,
},
{
name: "kernel",
size: 0x200000,
offset: 0x60000,
mask_flags: MTD_WRITEABLE,
},
{
name: "jffs2(11M)",
size: 0xB00000,
offset: 0x260000
},
{
name: "patameters",
size: 0x20000,
offset: 0xd60000
},
};
static const char *probes[] = { NULL };
#endif
static int mtd_parts_nb = 0;
static struct mtd_partition *mtd_parts = 0;
static int __init init_at91rm9200nor(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);
at91rm9200nor_map.virt = ioremap(WINDOW_ADDR, WINDOW_SIZE);
if (!at91rm9200nor_map.virt) {
printk(MSG_PREFIX "failed to ioremap\n");
return -EIO;
}
simple_map_init(&at91rm9200nor_map);
mymtd = 0;
type = rom_probe_types;
for(; !mymtd && *type; type++) {
mymtd = do_map_probe(*type, &at91rm9200nor_map);
}
if (mymtd) {
mymtd->owner = THIS_MODULE;
#ifdef CONFIG_MTD_PARTITIONS
mtd_parts_nb = parse_mtd_partitions(mymtd, probes, &mtd_parts, MTDID);
if (mtd_parts_nb > 0)
part_type = "detected";
if (mtd_parts_nb == 0)
{
mtd_parts = at91rm9200nor_partitions;
mtd_parts_nb = ARRAY_SIZE(at91rm9200nor_partitions);
part_type = "static";
}
#endif
add_mtd_device(mymtd);
if (mtd_parts_nb == 0)
printk(KERN_NOTICE MSG_PREFIX "no partition info available\n");
else
{
printk(KERN_NOTICE MSG_PREFIX
"using %s partition definition\n", part_type);
add_mtd_partitions(mymtd, mtd_parts, mtd_parts_nb);
}
return 0;
}
iounmap((void *)at91rm9200nor_map.virt);
return -ENXIO;
}
static void __exit cleanup_at91rm9200nor(void)
{
if (mymtd) {
del_mtd_device(mymtd);
map_destroy(mymtd);
}
if (at91rm9200nor_map.virt) {
iounmap((void *)at91rm9200nor_map.virt);
at91rm9200nor_map.virt = 0;
}
}
module_init(init_at91rm9200nor);
module_exit(cleanup_at91rm9200nor);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Marius Groeger <[email protected]>");
MODULE_DESCRIPTION("Generic configurable MTD map driver");