一、NAND原理
NAND 无地址空间,地址和数据的发送都依赖于LDATA[0:7]这一串数据总线。
不看随机页编程,看到从高位到低位的页,总共分为64个页面,每个页的组成是2K + 64 个byte,一个块的大小是(128K + 4K)byte,64页组成一块。
1.1 NAND Flash的编址
nand flash的的页的大小是(2048 + 64)byte,64这个数据是不参与编址的。
访问 nand flash:
发出命令:读、写、擦除
发出地址
传输数据
命令设置如下:
1.2 nand flash的访问
可以看看上面的模式选择,然后对应引脚进行操作,就可以进行读写操作。命令和数据就通过这些引脚来进行操作。但是不必直接操作这些引脚,可以通过寄存器来进行操作。
查看S3C2440的手册,可以看到如下的信息:
写命令寄存器就相当于NAND Flash寄存器命令周期,写地址寄存器就相当于NAND Flash寄存器的地址周期......
我们的NAND Flash是X8的,即是8位NAND Flash,对应2440上面的一些控制寄存器:
NAND Flash命令寄存器:
地址寄存器:
数据寄存器:
状态寄存器:
还有一些其他寄存器是与读写有关的。
操作这些寄存器,2440会自动驱动PIN脚给出信号。
二、u-boot中设置nand启动
2.1 建立init.c
这是NAND启动的初始化文件,里面包含了nand启动的初始化函数。代码添加到 borad/samdung/jz2440/ 目录下:
1 /* NAND FLASH控制器 */
2 #define NFCONF (*((volatile unsigned long *)0x4E000000))
3 #define NFCONT (*((volatile unsigned long *)0x4E000004))
4 #define NFCMMD (*((volatile unsigned char *)0x4E000008))
5 #define NFADDR (*((volatile unsigned char *)0x4E00000C))
6 #define NFDATA (*((volatile unsigned char *)0x4E000010))
7 #define NFSTAT (*((volatile unsigned char *)0x4E000020))
8
9 /* GPIO */
10 #define GPHCON (*(volatile unsigned long *)0x56000070)
11 #define GPHUP (*(volatile unsigned long *)0x56000078)
12
13 /* UART registers*/
14 #define ULCON0 (*(volatile unsigned long *)0x50000000)
15 #define UCON0 (*(volatile unsigned long *)0x50000004)
16 #define UFCON0 (*(volatile unsigned long *)0x50000008)
17 #define UMCON0 (*(volatile unsigned long *)0x5000000c)
18 #define UTRSTAT0 (*(volatile unsigned long *)0x50000010)
19 #define UTXH0 (*(volatile unsigned char *)0x50000020)
20 #define URXH0 (*(volatile unsigned char *)0x50000024)
21 #define UBRDIV0 (*(volatile unsigned long *)0x50000028)
22
23 #define TXD0READY (1<<2)
24
25
26 void nand_read_ll(unsigned int addr, unsigned char *buf, unsigned int len);
27
28 /* 判定是否是nor启动 */
29 static int isBootFromNorFlash(void)
30 {
31 volatile int *p = (volatile int *)0;
32 int val;
33
34 val = *p;
35 *p = 0x12345678;
36 if (*p == 0x12345678)
37 {
38 /* 写成功, 是nand启动 */
39 *p = val;
40 return 0;
41 }
42 else
43 {
44 /* NOR不能像内存一样写 */
45 return 1;
46 }
47 }
48
49 /* 拷贝代码到sdram */
50 void copy_code_to_sdram(unsigned char *src, unsigned char *dest, unsigned int len)
51 {
52 int i = 0;
53
54 /* 如果是NOR启动 */
55 if (isBootFromNorFlash())
56 {
57 while (i < len)
58 {
59 dest[i] = src[i];
60 i++;
61 }
62 }
63 else
64 {
65 //nand_init();
66 nand_read_ll((unsigned int)src, dest, len);
67 }
68 }
69
70 /* 清除BSS */
71 void clear_bss(void)
72 {
73 extern int __bss_start, __bss_end;
74 int *p = &__bss_start;
75
76 for (; p < &__bss_end; p++)
77 *p = 0;
78 }
79
80 /* nand初始化 */
81 void nand_init_ll(void)
82 {
83 #define TACLS 0
84 #define TWRPH0 1
85 #define TWRPH1 0
86 /* 设置时序 */
87 NFCONF = (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4);
88 /* 使能NAND Flash控制器, 初始化ECC, 禁止片选 */
89 NFCONT = (1<<4)|(1<<1)|(1<<0);
90 }
91
92 static void nand_select(void)
93 {
94 NFCONT &= ~(1<<1);
95 }
96
97 static void nand_deselect(void)
98 {
99 NFCONT |= (1<<1);
100 }
101
102 static void nand_cmd(unsigned char cmd)
103 {
104 volatile int i;
105 NFCMMD = cmd;
106 for (i = 0; i < 10; i++);
107 }
108
109 static void nand_addr(unsigned int addr)
110 {
111 unsigned int col = addr % 2048;
112 unsigned int page = addr / 2048;
113 volatile int i;
114
115 NFADDR = col & 0xff;
116 for (i = 0; i < 10; i++);
117 NFADDR = (col >> 8) & 0xff;
118 for (i = 0; i < 10; i++);
119
120 NFADDR = page & 0xff;
121 for (i = 0; i < 10; i++);
122 NFADDR = (page >> 8) & 0xff;
123 for (i = 0; i < 10; i++);
124 NFADDR = (page >> 16) & 0xff;
125 for (i = 0; i < 10; i++);
126 }
127
128 static void nand_wait_ready(void)
129 {
130 while (!(NFSTAT & 1));
131 }
132
133 static unsigned char nand_data(void)
134 {
135 return NFDATA;
136 }
137
138 void nand_read_ll(unsigned int addr, unsigned char *buf, unsigned int len)
139 {
140 int col = addr % 2048;
141 int i = 0;
142
143 /* 1. 选中 */
144 nand_select();
145
146 while (i < len)
147 {
148 /* 2. 发出读命令00h */
149 nand_cmd(0x00);
150
151 /* 3. 发出地址(分5步发出) */
152 nand_addr(addr);
153
154 /* 4. 发出读命令30h */
155 nand_cmd(0x30);
156
157 /* 5. 判断状态 */
158 nand_wait_ready();
159
160 /* 6. 读数据 */
161 for (; (col < 2048) && (i < len); col++)
162 {
163 buf[i] = nand_data();
164 i++;
165 addr++;
166 }
167
168 col = 0;
169 }
170
171 /* 7. 取消选中 */
172 nand_deselect();
173 }
修改 borad/samdung/jz2440/ 目录下的 makefile:
2.2 去掉代码重定位
去掉 -pie 选项:
注释掉第82行:
2.3 修改代码链接地址
Jz2440.h (includeconfigs)
2.4 修改链接脚本文件
U-boot.lds (archarmcpu) 链接脚本的修改,本版本u-boot将一个文件夹下面的c文件都链接成一个.o文件,名字为built-in.o,所以我们直接写上built-in.o即可。
将这两个built-in.o放入链接脚本当中:
2.5 修改 board_init_f
在crt0.S中设置nand 启动的代码:
这一段代码就是 nand 重定位代码了。CONFIG_SYS_TEXT_BASE 是自己设置的,可以自行设置大小。
接下来的代码就是跳转到 board_init_f_mem 和 board_init_f 中执行初始化。
init_sequence_f 链表中要注释掉下面两行,并更改:
2.6 修改重定位代码
首先在board_init_f(Board_f.c (common) )添加返回函数:
1 unsigned int board_init_f(ulong boot_flags)
2 {
3
4 gd->flags = boot_flags;
5 gd->have_console = 0;
6
7 if (initcall_run_list(init_sequence_f))
8 hang();
9 #if 0
10 #if !defined(CONFIG_ARM) && !defined(CONFIG_SANDBOX) &&
11 !defined(CONFIG_EFI_APP)
12 /* NOTREACHED - jump_to_copy() does not return */
13 hang();
14 #endif
15 #endif
16 return (unsigned int)(gd->new_gd); //添加的返回值,返回id供 board_init_r调用
17 }
board_init_f 函数原型记得在 include/common中也修改。
修改crt0.S 代码如下:
ENTRY(_main)
/*
* Set up initial C runtime environment and call board_init_f(0).
* 初始化C运行环境并且调用 board_init_f(0) 函数
*/
/*
* 初始化栈地址
*/
#if defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_STACK)
ldr sp, =(CONFIG_SPL_STACK)
#else
/* Generic-asm-offsets.h (includegenerated)
* #define GENERATED_GBL_DATA_SIZE 192
* JZ2440.h(includeconfig)
* #define PHYS_SDRAM_1 0x30000000
* #define CONFIG_SYS_SDRAM_BASE PHYS_SDRAM_1
* #define CONFIG_SYS_INIT_SP_ADDR (CONFIG_SYS_SDRAM_BASE + 0x1000 - GENERATED_GBL_DATA_SIZE)
*
* CONFIG_SYS_INIT_SP_ADDR = 0x30000000 + 0x1000 - 192(0xc0) = 0x30000f40