Linux移植随笔:让内核支持nor flash

内核:linux-2.6.37.3
nor flash芯片:SST39VF6401B
网上有文章说了如何让linux内核支持nor flash。不过那些转载的文章中没有头文件(因为使用了<尖括号>,在一些网页中是不会显示的,详细请参考HTML相关资料)。后来研究了类似的驱动文件,发现它们都是大同小异,只是在一些参数上有改变而已。本文中源代码基于那些转载文章的代码。

MTD设备驱动程序在./driver/mtd下面,分为nor flashnand flash两种。在chips中定义了几种访问nor的接口,包括cfi、jedec、map_ram和map_rom,而实际芯片所要添加的“驱动程序”放到maps目录下(比如本文使用的s3c2440-flash.c文件,详见文章后面)。从map这个单词来看,它描述的是一些映射信息,当然也包括了芯片信息,如总大小、块总数、访问接口等等。

这个过程其实是在内核代码树中添加自己的驱动的过程,当然是一些“标准”步骤了:在对应的代码树目录下,添加驱动源代码文件、在Makefile添加内容、在Kconfig添加内容,当然,还需要在make menuconfig里配置。

1、./driver/mtd/maps/Makefile
在这个Makefile最后添加:

1
obj-$(CONFIG_MTD_S3C2440) += s3c2440-flash.o

这个CONFIG_MTD_S3C2440由内核配置所得。

2、./driver/mtd/maps/Kconfig
在这个Kconfig文件最后添加(但在endmenu之前):

1
2
3
4
5
6
config MTD_S3C2440
 tristate "CFI Flash device mapped on S3C2440"
 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_S3C2440就是前面Makefile中的那个,注意,CONFIG_前缀是内核自己添加的。可以在编译内核后生成的autoconf.h文件中查看该宏定义。

3、内核配置
Device Drivers ---> <*> Memory Technology Device (MTD) support --->下面:

1
2
3
4
5
6
7
8
Mapping drivers for chip access  ---> 
 <*> CFI Flash device mapped on S3C2440   
RAM/ROM/Flash chip drivers --->
 <*> Detect flash chips by Common Flash Interface (CFI) probe
[*] Flash chip driver advanced configuration options 
[*]   Specific CFI Flash geometry selection 
[*]     Support 16-bit buswidth
[*]     Support 1-chip flash interleave

4、./driver/mtd/maps/s3c2440-flash.c
代码见文章后面。
这个文件主要是修改前面的几个宏定义。我特地添加一些调试语句。开始时我使用2.6.30.2内核,驱动中使用cfi_probe接口,但是没有显示分区信息,后来我添加多几个接口才发现它是使用map_rom。这个过程花费了一些时间。
在2.6.30.2内核中,启动信息如下:

1
2
3
4
5
6
7
8
9
10
S3C2440-NOR:0x00800000 at 0x01000000
S3C2440-NOR:func:init_s3c2440nor[120] mymtd:0 type:cfi_probe
S3C2440-NOR:func:init_s3c2440nor[120] mymtd:0 type:jedec_probe
S3C2440-NOR:func:init_s3c2440nor[120] mymtd:c39cb600 type:map_rom
S3C2440-NOR:using static partition definition
Creating 4 MTD partitions on "NOR Flash(SST39VF6401B) on S3C2440":
0x000000000000-0x000000030000 : "U-Boot"
0x000000030000-0x000000240000 : "Kernel"
0x000000240000-0x0000007f0000 : "RootFS(jffs2)"
0x0000007f0000-0x000000800000 : "Parameter"

可以看到,的确是使用map_rom接口。但是,当我使用2.6.37.2时,发现它使用的是cfi_probe接口,启动信息如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
S3C2440-NOR:0x00800000 at 0x01000000
NOR Flash(SST39VF6401B) on S3C2440: Found 1 x16 devices at 0x0 in 16-bit bank. 

Manufacturer ID 0x0000bf Chip ID 0x00236d
number of CFI chips: 1
## LL debug S3C2440-NOR:func:init_s3c2440nor[121] mymtd:c3a19200 type:cfi_probe
mtd: Giving out device 0 to NOR Flash(SST39VF6401B) on S3C2440
S3C2440-NOR:using static partition definition
Creating 4 MTD partitions on "NOR Flash(SST39VF6401B) on S3C2440":
0x000000000000-0x000000030000 : "U-Boot"
mtd: Giving out device 1 to U-Boot
0x000000030000-0x000000240000 : "Kernel"
mtd: Giving out device 2 to Kernel
0x000000240000-0x0000007f0000 : "Rootfs(jffs2)"
mtd: Giving out device 3 to Rootfs(jffs2)
0x0000007f0000-0x000000800000 : "Parameter"
mtd: Giving out device 4 to Parameter

与前面不同的是,它打印了芯片的信息,如厂商ID、芯片ID等等。
这是我想不通的地方,也是我采用新内核的原因。
不过,在这片nor flash上使用jffs2文件系统却不成功,出现了大量的警告信息,提示说制作根文件系统时的erase block有问题。 s3c2440-flash.c源代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
/*
* $Id: s3c2410.c,v 1.00 2006/12/05 10:18:14 gleixner Exp $
*
* Handle mapping of the NOR flash on Cogent S3C2410 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.
*/

/*
*                 Late Lee
* This file is modified and the file name has changed to 's3c2440-flash.c'.
* The SMDK2440 board has only one flash bank which is a 64Mbit(4Mx16) SST SST39VF6401B;
* 128 x 64 KiB blocks.
*
* This code is GPLed.
*/

#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>
#include <linux/mtd/cfi.h>

#ifdef CONFIG_MTD_PARTITIONS
#include <linux/mtd/partitions.h>
#endif

#define WINDOW_ADDR 0x01000000       /* physical properties of flash */
#define WINDOW_SIZE 0x800000 /* 8MB */
#define BUSWIDTH    2 /* 2*8 = 16 */
#define FLASH_BLOCKSIZE_MAIN        0x10000  /* 64KB(0x10000) 128 blocks */
#define FLASH_NUMBLOCKS_MAIN        128

/* can be "cfi_probe", "jedec_probe", "map_rom", NULL }; */
#define PROBETYPES { "cfi_probe", "jedec_probe", "map_rom", NULL }

#define MSG_PREFIX "S3C2440-NOR:"    /* prefix for our printk()'s */
#define MTDID      "s3c2440-nor"     /* for mtdparts= partitioning */

#define DEBUG_FLASH
#ifdef DEBUG_FLASH
#define flash_debug(fmt, args...) printk(KERN_NOTICE "## LL debug " MSG_PREFIX  fmt, ##args)
#else
#define flash_debug(fmt, args...)
#endif

static struct mtd_info *mymtd;

struct map_info s3c2440nor_map = {
        .name = "NOR Flash(SST39VF6401B) on S3C2440",
        .size = WINDOW_SIZE,
        .bankwidth = BUSWIDTH,
        .phys = WINDOW_ADDR,
};

#ifdef CONFIG_MTD_PARTITIONS

/*
* MTD partitioning stuff
*/
static struct mtd_partition static_partitions[] = {
         /*0: U-Boot: 0-0x30000 0x30000=192KB*/
        {
        .name = "U-Boot",
        .size = 0x030000,
        .offset = 0x0,
        },
         /*1: Kernel: 0x30000-0x240000 0x210000=2112KB=2.0625MB*/
        {
        .name = "Kernel",
        .size = 0x210000,
        .offset = 0x30000,
        },
         /*2: Rootfs(jffs2): 0x240000-0x7f0000 0x5b0000=5824KB=5.6875MB*/
        {
        .name = "Rootfs(jffs2)",
        .size = 0x5b0000,
        .offset = 0x240000,
        },
         /*3: U-Boot Parameters: 0x7f0000-0x800000 0x10000=64KB*/
        {.name = "Parameter",
        .size = 0x010000,
        .offset = 0x7f0000,
        },
};

//static const char *probes[] = { "RedBoot", "cmdlinepart", NULL };
static const char *probes[] = { NULL };

#endif

static int mtd_parts_nb = 0;
static struct mtd_partition *mtd_parts = 0;

int __init init_s3c2440nor(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);
        s3c2440nor_map.virt = ioremap(WINDOW_ADDR, WINDOW_SIZE);
        if (!s3c2440nor_map.virt) {
                   printk(MSG_PREFIX "failed to ioremap\n");
                   return -EIO;
        }

        simple_map_init(&s3c2440nor_map);
        mymtd = 0;
        type = rom_probe_types;
        for(; !mymtd && *type; type++) {
                   mymtd = do_map_probe(*type, &s3c2440nor_map);
                   flash_debug("func:%s[%d] mymtd:%x type:%s\n", __func__, __LINE__, mymtd, *type);
        }

        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 = static_partitions;
                              mtd_parts_nb = ARRAY_SIZE(static_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 *)s3c2440nor_map.virt);
        return -ENXIO;
}

static void __exit cleanup_s3c2440nor(void)
{
        if (mymtd) {
                 del_mtd_device(mymtd);
                 map_destroy(mymtd);
        }
        if (s3c2440nor_map.virt) {
                 iounmap((void *)s3c2440nor_map.virt);
                 s3c2440nor_map.virt = 0;
        }
}

module_init(init_s3c2440nor);
module_exit(cleanup_s3c2440nor);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("GengYaoJun ");
MODULE_DESCRIPTION("Generic configurable MTD map driver");