一、准备移植
1.1)平台及硬件介绍
u-boot版本:u-boot-2011-03
Linux平台:Ubutu 10.10
交叉编译工具:arm-linux-gcc-4.3.2
$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
arm-linux-gcc-4.3.2.tgz下载在Linux公社的1号FTP服务器里,下载地址:
FTP地址:ftp://www.linuxidc.com
用户名:www.linuxidc.com
密码:www.muu.cc
在 2011年LinuxIDC.com\4月\Ubuntu 10.10 建立交叉编译工具4.3.2
下载方法见 http://www.linuxidc.net/thread-1187-1-1.html
U-Boot源代码下载地址 http://www.linuxidc.com/Linux/2011-07/38897.htm
$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
目标板子: Micro2440
CPU: S3C2440
SDRAM: 64M
Nor Flash: 39F1601 2M
Nand Flash: K9F2G08 256M
网卡: DM9000EP
1.2)实现功能:
a):支持NORFLASH读写
b):支持NANDFLASH读写
c):支持NORFLASH引导Linux内核
d):支持NANDFLASH引导内核
e):支持DM9K网卡
f):支持TFTP、TFTP下载
g):支持YAFFS文件系统
h):支持JFFS2文件系统
1.3)获取u-boot-2011-03源码 u-boot-2011-03.tar.bz在:点击获取u-boot-2011-03源码
Uboot启动流程图如下
二、建立属于自己的开发板项目<eilian240>
2.1)源码解压进入Uboot主目录<大家都懂的>
#tar jaxvf u-boot-2010.03.tar.bz2
#cd cd u-boot-2010.03
2.2)克隆目标板<借签smdk2410>
a)创建eilian240文件
#cd board/samsung/
#mkdir eilian240
#cp -fr smdk2410 eilian240
#cd eilian240
#mv smdk2410.c eilian240.c
2.3)修改eilian240下的Makefile<为什么我想大家都懂的>
修改COBJS := smdk2410.o flash.o --->COBJS := eilian240.o flash.o
2.4)创建板级头文件
#cd include/configs/
#cp -fr smdk2410.h eilian240.h
2.5)修改Uboot根目录下的Makefile文件
#vi Makefile
修改约164行:CROSS_COMPILE?= arm-linux-
在约3049行添加:
eilian240_config : unconfig@$(MKCONFIG) $(@:_config=) arm arm920t eilian240 samsung s3c24x0
*说明:arm :CPU的架构(ARCH)
arm920t:CPU的类型
eilian240:对应在board目录下建立新的开发板项目的目录
samsung:新开发板项目目录的上级目录,如直接在board下建立新的开发板项目的目录,则这里就为NULL
s3c24x0:CPU型号
2.6)测试编译新建的<eilian240>项目
a)回到Uboot主目录
b)测试:
#make eilian240_config
如果出现Configuring for eilian240 board...表示设置成功
#make
编译完成后在uboot主目录下生成uboot.bin文件:至此uboot移植第一步完成
三、修改时钟频率和中断的配置
Notes:下面绿色色部分代码既是需要修改的代码
UBOOT-2010-03在S3C2440上的移植<一>------------项目搭建
http://www.linuxidc.com/Linux/2011-11/46993.htm
3.1)修改eilian240开发板u-boot第一个要运行的程序cpu/arm920t/start.S(即u-boot的 stage1部分)
3.1.1) 添加CPU频率初始化设置:
大概在star.S文件的第146行代码后面添加如下内容
/***定义时钟配置寄存器****change by eilianlau*************************/
#define CLK_CTL_BASE 0x4C000000
#define MDIV_405 0x7f << 12
#define PSDIV_405 0x21
#define MDIV_200 0xa1 << 12
#define PSDIV_200 0x31
/***************这些寄存器的定义S3C2440数据手册上是说的再清楚不过了如果不知道怎么配置请看下面贴图
3.1.2)添加中断禁止部分: 因为2410和2440的中断配置不同
# if defined(CONFIG_S3C2410)
ldr r1, =0x3ff
ldr r0, =INTSUBMSK
str r1, [r0]
#endif //
/***添加中断禁止部分****changeby eilianlau*************************/
# if defined(CONFIG_S3C2440)
ldr r1, =0x7fff
ldr r0, =INTSUBMSK
str r1, [r0]
# endif
3.1.3) 修改时钟设置(2440的主频为405MHz)
原来的时钟设置函数是
/* FCLK:HCLK:PCLK = 1:2:4 */
/* default FCLK is 120 MHz ! */
ldr r0, =CLKDIVN
mov r1, #3
str r1, [r0]
修改上述代码如下
/***修改时钟设置(2440的主频为405MHz)****change by eilianlau***FCLK:HCLK:PCLK=1:4:8******/
/*********************************/
#if defined(CONFIG_S3C2440)
/* FCLK:HCLK:PCLK = 1:4:8 */
ldr r0, =CLKDIVN
mov r1, #5
str r1, [r0]
mrc p15, 0, r1, c1, c0, 0
orr r1, r1, #0xc0000000
mcr p15, 0, r1, c1, c0, 0
/****上面这三句是非常的重要的对于S3C2440,CLKDIVN的第[2:1]位为HDIVN,如果HDIVN非0,CPU总线模 式应该从“fast bus mode”转换为“asynchronous bus mode”,可以通过上面的3句程序实现。否则
CPU的工作频率将自动变成HCLK,不再是FCLK。这对SDRAM的初始化至关重要。****/
mov r1, #CLK_CTL_BASE
mov r2, #MDIV_405
add r2, r2, #PSDIV_405
str r2, [r1, #0x04] /* MPLLCON*/
/************************************/
#else
/* FCLK:HCLK:PCLK = 1:2:4 */
/* default FCLK is 120 MHz ! */
ldr r0, =CLKDIVN
mov r1, #3
str r1, [r0]
/*************************************/
mrc p15, 0, r1, c1, c0, 0
orr r1, r1, #0xc0000000
mcr p15, 0, r1, c1, c0, 0
mov r1, #CLK_CTL_BASE
mov r2, #MDIV_200
add r2, r2, #PSDIV_200
str r2, [r1, #0x04]
#endif
/**************没什么可说的还是S3C2440数据手册********************/
#endif /* CONFIG_S3C24X0 */
3.2)修改cpu/arm920t/s3c24x0/ speed.c
我直接添代码吧<不知道要说什么。。>
3.2.1)修改static ulong get_PLLCLK(int pllreg)
static ulong get_PLLCLK(int pllreg)
{
struct s3c24x0_clock_power *clk_power =s3c24x0_get_base_clock_power();
ulong r, m, p, s;
if (pllreg == MPLL)
r = readl(&clk_power->MPLLCON);
else if (pllreg == UPLL)
r = readl(&clk_power->UPLLCON);
else
hang();
m = ((r & 0xFF000) >> 12) + 8;
p = ((r & 0x003F0) >> 4) + 2;
s = r & 0x3;
#if defined(CONFIG_S3C2440)
if(pllreg == MPLL)
{ //参考S3C2440芯片手册上的公式:PLL=(2 * m * Fin)/(p * 2s)
return((CONFIG_SYS_CLK_FREQ * m * 2)/ (p << s));
}
#endif
return (CONFIG_SYS_CLK_FREQ * m) / (p << s);
}
3.2.2)修改ulong get_FCLK(void)
/* return HCLK frequency */
ulong get_HCLK(void)
{
struct s3c24x0_clock_power *clk_power =s3c24x0_get_base_clock_power();
#if defined(CONFIG_S3C2440)
return(get_FCLK()/4);
#endif
return (readl(&clk_power->CLKDIVN) & 2) ?get_FCLK() / 2 : get_FCLK();
}
3.3)修改board/Samsung/eilian240/ eilian240.c
3.3.1)修改FCLK代码中带详细解释
/**#define FCLK_SPEED 1 *默认是加载FCLK_SPEED==1但2440的FCLK输出时钟为405Mhz该设置请参考2440数据手册255页那个表*/
#define FCLK_SPEED 2
/**change by eilianlau********/
#if FCLK_SPEED==0 /* Fout = 203MHz, Fin =12MHz for Audio */
#define M_MDIV 0xC3
#define M_PDIV 0x4
#define M_SDIV 0x1
#elif FCLK_SPEED==1 /* Fout = 202.8MHz */
#define M_MDIV 0xA1
#define M_PDIV 0x3
#define M_SDIV 0x1
/**change by eilianlau********/
#elif FCLK_SPEED==2 /* Fout = 405MHz */
#define M_MDIV 0x7f
#define M_PDIV 0x2
#define M_SDIV 0x1
#endif
/**change by eilianlau********/
3.3.2)修改USB的时钟
/**#define USB_CLOCK 1*道理同上*/
#define USB_CLOCK 2
#if USB_CLOCK==0
#define U_M_MDIV 0xA1
#define U_M_PDIV 0x3
#define U_M_SDIV 0x1
#elif USB_CLOCK==1
#define U_M_MDIV 0x48
#define U_M_PDIV 0x3
#define U_M_SDIV 0x2
/**change by eilianlau********/
#elif USB_CLOCK==2
#define U_M_MDIV 0x38
#define U_M_PDIV 0x2
#define U_M_SDIV 0x2
#endif
3.3.3)修改引导Linux内核的机器码<机器码必须和Linux内核中的机器码相同 这个是自己可以定义> cpu/arm920t/s3c24x0/ speed.c
/* arch number of eilian240-Board */
/* 这个机器码用于和Linux内核配对*/
gd->bd->bi_arch_number =MACH_TYPE_EILIAN240;
/* adress of boot parameters */
gd->bd->bi_boot_params = 0x30000100;
icache_enable();
3.4)定义3.3.3的机器码MACH_TYPE_EILIAN240
3.4.1)修改u-boot-2010.03/include/asm-arm/mach-types.h <机器码都定义在这个文件中呢。。>
在第2702行添加下面的宏定义就O了
#defineMACH_TYPE_EILIAN240 8000
3.5)在include/configs/eilian240.h头文件中添加之前用到的CONFIG_S3C2440宏
第40行添加代码如下:
#define CONFIG_S3C2440 1 /* on aeilian240 Board */
/******************************************************************/
3.6)另外为了更好理解S3C2440的时钟体系 上传几个图来理解希望对大家有所帮助<移植S3C3440芯片手册是必须要要参看的>
S3C2440时钟体系<一>
S3C2440时钟体系<二>
S3C2440时钟启动<一>
S3C2440时钟启动<二>
S3C2440时钟时钟相关寄存器LOCKTIME
S3C2440时钟时钟相关寄存器MPLLCON
S3C2440时钟时钟相关寄存器CLKDIVN
至此硬件初始化的工作就完成了。。可以测试编译下看有没有错误<编译通过 No problem> 如发现有错误请指出 十分感谢。。。
四、自动识别启动模式Nand Or NorNotes:1)上文连接
UBOOT-2010-03在S3C2440上的移植<二>------------硬件初始化
http://www.linuxidc.com/Linux/2011-11/46993p2.htm
Notes:2)下面代码段红色部分为删除部分
Notes:3)下面代码段绿色部分为添加部分
4.1)Notes:以下资源来自大神Tekkaman Ninja博客点击查看原文
移植之前还是请大家先看S3C2440数据手册<第五章 存储器控制器>中的<Figure 5-1. S3C2440A Memory Map after Reset>(也就是那个映射图),从理论上来讲,对于0x40000000以后的内存,只有在Nor boot的时候才存在;而在nand
boot 的时候他被映射到了0x00000000,在 0x40000000以后不存在内存 。如果我们在启动的时候,将一些特定的数据写入0x40000000~0x40001000之间,那么按照数据手册上的说法,如果回读的结果和写入的一致说明是nor boot,否则就是nand boot! <写这个文章的时候我又看了一次。。。。。。。。。>从上图我们可以看出,无论是Nor boot还是nand boot ,这4K的内部SRAM都被映射到了0x40000000,而在nand boot的时候,这块内存同时还被映射到了0x00000000。那么一开始提出的办法就不可行了,而且在nand boot 的时候,写入0x40000000~0x40001000还会破坏自身的程序。
但是通过上面的图,我想到了解决的办法:
在启动的时候,用程序将0x40000000~0x40001000中的某些位置清零,如果回读0x00000000~0x00001000中的相应位置后为零,说明是Nand boot,如果是原来的数据(一定要选非零的位置)就是Nor boot。判断完后如果是nand boot,还要恢复被改动的数据,再进入自拷贝阶段。
只要检测的位置合理,这方法是可行的。现在的关键是选什么位置的数据最好呢?经过查看源码,我选择了在start.S文件开头,全局中断向量之后的:
|
选这个数据作为检测位置的理由如下:
(1)他是非零数,而且数据是确定的:
0xdeadbeef;
(2)他的位置是固定的: 0x0000003c ( 0x4000003c );
(3)他在检测程序之前,不会影响程序的向下运行;
(4)他不属于程序,他是一个程序中的魔数(Magic Number),用魔数来检测也比较合理 。
所以我最后的检测步骤是:
在启动的时候,将 0x4000003c位置 开始的四个字节清零,然后读取0x0000003c位置 开始的四个字节 。如果回读的结果为零,说明是 nand boot ,否则就是Nor boot(为了保险还可以判断是否为
0xdeadbeef
,不是的话就说明有未知错误,死循环!
)。
但是最后有一点很重要:如果是Nand boot,必须要复原清零的数据。
原因是:在nand boot过后,会核对内部SRAM中的4K程序,和从Nand中拷贝到SDRAM的前4K程序是否一致,如果不一致会进入死循环。
/**************好了看完这一对白虽然明白了很多但是跟随着的问题也多了很多,我就在想.balignl 16,0xdeadbeef
这个东东究竟是起到什么作用的,你又怎么知道它的位置就是固定的而且又是0x0000003c(0x4000003c)呢?为此我纠结了好久还好通过一番纠结我终于找到了答案<这得感谢我哥:baiu and google>1)点击查看答案and2)点击查看答案 那么接下来是时候动手代码的移植了
4.1.1)打开之前我们修改的那个start.S文件 第90行左右添加一个Flash启动标志
/************************标志位从Nand启动时将其设置为0,从Nor启动时将其设置为1*****/
.globl bBootFrmNORFlash
bBootFrmNORFlash:
.word 0
4.1.2)判断当前代码位置,如果在内存,直接跳到stack_setup
/*
* we do sys-critical inits only at reboot,
* not when booting from ram!
*/
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
bl cpu_init_crit
#endif /***找到这段代码在下面添加:***/
/***************** CHECK_CODE_POSITION ******************************************/
adr r0, _start /* r0 <- current position of code */
ldr r1, _TEXT_BASE /* test if we run from flash or RAM */
cmp r0, r1 /* don't reloc during debug */
beq stack_setup
/***************** CHECK_CODE_POSITION ******************************************/
4.1.3)如果代码当前位置不在内存中,就判断启动方式为Nand Flash或者Nor Flash
Notes:没有说明就默认在以上代码的后面添加
/***************** CHECK_BOOT_FLASH ******************************************/
ldr r1, =( (4<<28)|(3<<4)|(3<<2) ) /* address of Internal SRAM 0x4000003C*/
mov r0, #0 /* r0 = 0 */
str r0, [r1]
mov r1, #0x3c /* address of men 0x0000003C*/
ldr r0, [r1]
cmp r0, #0
bne relocate
/* recovery */
ldr r0, =(0xdeadbeef)
ldr r1, =( (4<<28)|(3<<4)|(3<<2) )
str r0, [r1]
/***************** CHECK_BOOT_FLASH ******************************************/
4.1.4)在Nand Flash中启动的话,那么Nand Flash搬移代码如下:
Notes:定义u-boot在Nand flash中存放的长度为#define LENGTH_UBOOT 0x100000<1M>,可以方便修改u-boot因为裁剪和增添大小的改变而占的长度。
/***************** NAND_BOOT *************************************************/
#define LENGTH_UBOOT 0x100000
#define NAND_CTL_BASE 0x4E000000
#ifdef CONFIG_S3C2440
/* Offset */
#define oNFCONF 0x00
#define oNFCONT 0x04
#define oNFCMD 0x08
#define oNFSTAT 0x20
@ reset NAND
mov r1, #NAND_CTL_BASE
ldr r2, =( (7<<12)|(7<<8)|(7<<4)|(0<<0) )
str r2, [r1, #oNFCONF]
ldr r2, [r1, #oNFCONF]
ldr r2, =( (1<<4)|(0<<1)|(1<<0) ) @ Active low CE Control
str r2, [r1, #oNFCONT]
ldr r2, [r1, #oNFCONT]
ldr r2, =(0x6) @ RnB Clear
str r2, [r1, #oNFSTAT]
ldr r2, [r1, #oNFSTAT]
mov r2, #0xff @ RESET command
strb r2, [r1, #oNFCMD]
mov r3, #0 @ wait
nand1:
add r3, r3, #0x1
cmp r3, #0xa
blt nand1
nand2:
ldr r2, [r1, #oNFSTAT] @ wait ready
tst r2, #0x4
beq nand2
ldr r2, [r1, #oNFCONT]
orr r2, r2, #0x2 @ Flash Memory Chip Disable
str r2, [r1, #oNFCONT]
@ get read to call C functions (for nand_read())
ldr sp, DW_STACK_START @ setup stack pointer
mov fp, #0 @ no previous frame, so fp=0
@ copy U-Boot to RAM
ldr r0, =TEXT_BASE
mov r1, #0x0
mov r2, #LENGTH_UBOOT
bl nand_read_ll
tst r0, #0x0
beq ok_nand_read
bad_nand_read:
loop2:
b loop2 @ infinite loop
ok_nand_read:
@ verify
mov r0, #0
ldr r1, =TEXT_BASE
mov r2, #0x400 @ 4 bytes * 1024 = 4K-bytes
go_next:
ldr r3, [r0], #4
ldr r4, [r1], #4
teq r3, r4
bne notmatch
subs r2, r2, #4
beq stack_setup
bne go_next
notmatch:
loop3:
b loop3 @ infinite loop
#endif
/***************** NAND_BOOT *************************************************/
4.1.5)在Nor Flash中启动的话,那么Nor Flash搬移代码如下:
/***************** NOR_BOOT *************************************************/
relocate: /* relocate U-Boot to RAM */
/*********** CHECK_FOR_MAGIC_NUMBER***************/
ldr r1, =(0xdeadbeef)
cmp r0, r1
bne loop3
/*********** CHECK_FOR_MAGIC_NUMBER***************/
adr r0, _start /* r0 <- current position of code */
ldr r1, _TEXT_BASE /* test if we run from flash or RAM */
ldr r2, _armboot_start
ldr r3, _bss_start
sub r2, r3, r2 /* r2 <- size of armboot */
add r2, r0, r2 /* r2 <- source end address */
copy_loop:
ldmia r0!, {r3-r10} /* copy from source address [r0] */
stmia r1!, {r3-r10} /* copy to target address [r1] */
cmp r0, r2 /* until source end addreee [r2] */
ble copy_loop
SetBootFlag:
ldr r0, =bBootFrmNORFlash
mov r1, #1
str r1, [r0]
/***************** NOR_BOOT *************************************************/
4.1.6)删除下面这段代码
//#ifndef CONFIG_SKIP_RELOCATE_UBOOT
//relocate: /* relocate U-Boot to RAM */
// adr r0, _start /* r0 <- current position of code */
// ldr r1, _TEXT_BASE /* test if we run from flash or RAM */
// cmp r0, r1 /* don't reloc during debug */
// beq stack_setup
// ldr r2, _armboot_start
// ldr r3, _bss_start
// sub r2, r3, r2 /* r2 <- size of armboot */
// add r2, r0, r2 /* r2 <- source end address */
//copy_loop:
// ldmia r0!, {r3-r10} /* copy from source address [r0] */
// stmia r1!, {r3-r10} /* copy to target address [r1] */
// cmp r0, r2 /* until source end addreee [r2] */
// ble copy_loop
//#endif /* CONFIG_SKIP_RELOCATE_UBOOT */
4.2.1)在_start_armboot: .word start_armboot 后面添加:
#define STACK_BASE 0x33f00000
#define STACK_SIZE 0x10000
.align 2
DW_STACK_START: .word STACK_BASE+STACK_SIZE-4
/**********************************到此完了。。。。********/
Notes1):如果你不是第一次移植UBOOT那么建议好好的理解一下上面这些代码<当然加入你还是第一次的话那么好了直接COPY吧很管用的>
Notes2):上面用到了一个nand_read_ll函数,该函数用来读NANDFLASH的<下一篇就是实现这个UBOOT对NAND的支持了先休息一下吧>
四、修改 Nand flash 相关代码,使 U-BOOT 支持 Nand flash
上文连接:
UBOOT-2010-03在S3C2440上的移植<三>------------自动识别启动模式Nand Or Nor
http://www.linuxidc.com/Linux/2011-11/46993p3.htm
5.1)start.S中Nand Boot需要nand_read_ll函数, 在board/samsung/eilian240下新建文件nand_read.c
文件内容如下:
/*
* nand_read.c: Simple NAND read functions for booting from NAND
*
* This is used by cpu/arm920/start.S assembler code,
* and the board-specific linker script must make sure this
* file is linked within the first 4kB of NAND flash.
*
* Taken from GPLv2 licensed vivi bootloader,
* Copyright (C) 2002 MIZI Research, Inc.
*
* Author: Hwang, Chideok <hwang@mizi.com>
* Date : $Date: 2004/02/04 10:37:37 $
*
* u-boot integration and bad-block skipping (C) 2006 by OpenMoko, Inc.
* Author: Harald Welte <laforge@openmoko.org>
*/
#include <common.h>
#include <linux/mtd/nand.h>
#define __REGb(x) (*(volatile unsigned char *)(x))
#define __REGw(x) (*(volatile unsigned short *)(x))
#define __REGi(x) (*(volatile unsigned int *)(x))
#define NF_BASE 0x4e000000
#define NFCONF __REGi(NF_BASE + 0x0)/**0x4e000000**NAND配置寄存器**/
#define NFCONT __REGi(NF_BASE + 0x4)/**0x4e000004**NAND控制寄存器**/
#define NFCMD __REGb(NF_BASE + 0x8)/*0x4e000008**NAND命令寄存器Bit7-0存放芯片内设命令值*/
#define NFADDR __REGb(NF_BASE + 0xc)/**0x4e00000C**NAND地址寄存器Bit7-0存放**/
#define NFDATA __REGb(NF_BASE + 0x10)/**0x4e000010**NAND数据寄存器8位IO**/
#define NFDATA16 __REGw(NF_BASE + 0x10)/**0x4e000010**NAND数据寄存器16位IO**/
#define NFSTAT __REGb(NF_BASE + 0x20)/**0x4e000020**NAND状态寄存器**/
#define NFSTAT_BUSY 1
#define nand_select() (NFCONT &= ~(1 << 1)) /***芯片片选**/
#define nand_deselect() (NFCONT |= (1 << 1))/**取消芯片片选**/
#define nand_clear_RnB() (NFSTAT |= (1 << 2)) /**NFSTAT状态寄存器的Bit2 写1
/**************上面这些都是一些S3C2440NANDFLASH寄存器的定义怎么定义数据手册很详细的*******/
/****我再一次的打开了S3C2440数据手册***/
static inline void nand_wait(void)
{
int i;
while (!(NFSTAT & NFSTAT_BUSY))
for (i=0; i<10; i++);
}
struct boot_nand_t {
int page_size;
int block_size;
int bad_block_offset;
// unsigned long size;
};
static int is_bad_block(struct boot_nand_t * nand, unsigned long i)
{
unsigned char data;
unsigned long page_num;
nand_clear_RnB();
if (nand->page_size == 512) {
NFCMD = NAND_CMD_READOOB; /* 0x50 */
NFADDR = nand->bad_block_offset & 0xf;
NFADDR = (i >> 9) & 0xff;
NFADDR = (i >> 17) & 0xff;
NFADDR = (i >> 25) & 0xff;
} else if (nand->page_size == 2048) {
page_num = i >> 11; /* addr / 2048 */
NFCMD = NAND_CMD_READ0;
NFADDR = nand->bad_block_offset & 0xff;
NFADDR = (nand->bad_block_offset >> 8) & 0xff;
NFADDR = page_num & 0xff;
NFADDR = (page_num >> 8) & 0xff;
NFADDR = (page_num >> 16) & 0xff;
NFCMD = NAND_CMD_READSTART;
} else {
return -1;
}
nand_wait();
data = (NFDATA & 0xff);
if (data != 0xff)
return 1;
return 0;
}
static int nand_read_page_ll(struct boot_nand_t * nand, unsigned char *buf, unsigned long addr)
{
unsigned short *ptr16 = (unsigned short *)buf;
unsigned int i, page_num;
nand_clear_RnB();
NFCMD = NAND_CMD_READ0;
if (nand->page_size == 512) {
/* Write Address */
NFADDR = addr & 0xff;
NFADDR = (addr >> 9) & 0xff;
NFADDR = (addr >> 17) & 0xff;
NFADDR = (addr >> 25) & 0xff;
} else if (nand->page_size == 2048) {
page_num = addr >> 11; /* addr / 2048 */
/* Write Address */
NFADDR = 0;
NFADDR = 0;
NFADDR = page_num & 0xff;
NFADDR = (page_num >> 8) & 0xff;
NFADDR = (page_num >> 16) & 0xff;
NFCMD = NAND_CMD_READSTART;
} else {
return -1;
}
nand_wait();
for (i = 0; i < (nand->page_size>>1); i++) {
*ptr16 = NFDATA16;
ptr16++;
}
return nand->page_size;
}
static unsigned short nand_read_id()
{
unsigned short res = 0;
NFCMD = NAND_CMD_READID;
NFADDR = 0;
res = NFDATA;
res = (res << 8) | NFDATA;
return res;
}
extern unsigned int dynpart_size[];
/* low level nand read function */
int nand_read_ll(unsigned char *buf, unsigned long start_addr, int size)
{
int i, j;
unsigned short nand_id;
struct boot_nand_t nand;
/* chip Enable */
nand_select();
nand_clear_RnB();
for (i = 0; i < 10; i++) ;
nand_id = nand_read_id();
if (0) { /* dirty little hack to detect if nand id is misread */
unsigned short * nid = (unsigned short *)0x31fffff0;
*nid = nand_id;
}
if (nand_id == 0xec76 || /* Samsung K91208 */
nand_id == 0xad76 ) { /*Hynix HY27US08121A*/
nand.page_size = 512;
nand.block_size = 16 * 1024;
nand.bad_block_offset = 5;
// nand.size = 0x4000000;
} else if (nand_id == 0xecf1 || /* Samsung K9F1G08U0B */
nand_id == 0xecda || /* Samsung K9F2G08U0B */
nand_id == 0xecd3 ) { /* Samsung K9K8G08 */
nand.page_size = 2048;
nand.block_size = 128 * 1024;
nand.bad_block_offset = nand.page_size;
// nand.size = 0x8000000;
} else {
return -1; // hang
}
if ((start_addr & (nand.block_size-1)) || (size & ((nand.block_size-1))))
return -1; /* invalid alignment */
for (i=start_addr; i < (start_addr + size);) {
#ifdef CONFIG_S3C2410_NAND_SKIP_BAD
if (i & (nand.block_size-1)== 0) {
if (is_bad_block(&nand, i) ||
is_bad_block(&nand, i + nand.page_size)) {
/* Bad block */
i += nand.block_size;
size += nand.block_size;
continue;
}
}
#endif
j = nand_read_page_ll(&nand, buf, i);
i += j;
buf += j;
}
/* chip Disable */
nand_deselect();
return 0;
}
/**********************接下来我想对这些代码进行分析这才是我本次移植的目的*******************/
六、了解NANDFLASH的一些硬件特性
6.1.1)nand flash layout
6.1.2)Nand Flash的物理存储单元的阵列组织结构以本开发板上的K9F2G08X0A为例:
6.1.3) Block 块
一个 Nand Flash (的 chip,芯片) 由很多个块(Block)组成,块的大小一般是 128KB, 256KB, 512KB,此处是 128KB。其他的小于 128KB 的,比如 64KB,一般都是下面将要介绍到的small block的Nand Flash。 块 Block,是Nand Flash的擦除操作的基本/最小单位。
6.1.4) Page 页
每个块里面又包含了很多页(page) 。每个页的大小,对于现在常见的Nand Flash多数是2KB,最新的Nand Flash的是4KB、8KB等,这类的页大小大于 2KB的Nand Flash,被称作 big block的 Nand Flash,对应的发读写命令地址,一共 5个周期(cycle),而老的 Nand Flash,页大小是 256B,512B,这类的 Nand Flash被称作 small block,地址周期只有 4个。 页 Page,是读写操作的基本单位。 不过,也有例外的是,有些 Nand Flash支持 subpage(1/2 页或 1/4页)子页的读写操作,不过一般很少见。
6.1.5) oob / Redundant Area / Spare Area
每一个页,对应还有一块区域,叫做空闲区域(spare area)/冗余区域(redundant area),而Linux 系统中,一般叫做 OOB(Out Of Band),这个区域,是最初基于Nand Flash的硬件特性:数据在读写时候相对容易错误,所以为了保证数据的正确性,必须要有对应的检测和纠错机制,此机制被叫做 EDC(Error Detection Code)/所以设计了多余的区域,用于放置数据的校验值。Oob 的读写操作,一般是随着页的操作一起完成的,即读写页的时候,对应地就读写了 oob。
关于 oob具体用途,总结起来有:
标记是否是坏快
存储ECC数据
存储一些和文件系统相关的数据。如 jffs2 就会用到这些空间存储一些特定信息,而yaffs2 文件系统,会在 oob中,存放很多和自己文件系统相关的信息
6.1.6)NANDFLASH常用命令设置
从上图可以看到,如果要实现读一个页的数据,就要发送 Read的命令,而且是分两个周期(Cycle),即分两次发送对应的命令,第一次是 0x00h,第二次是 0x30h,而两次命令中间,需要发送对应的你所要读取的页的地址。
对应地,其他常见的一些操作,比如写一个页的数据(Page Program),就是先发送 0x80h,然后发生要写入的地址,再发送0x10h。
6.1.7)PIN DESCRIPTION
对上表进行中文翻译。。。。。。。。
Notes:#代表低电平有效
6.1.8)理解NANDFLASH的操作时序图以Read Operation为例
Notes:此图来自三星的型号 K9F2G08的 Nand Flash的数据手册(datasheet)。
我们来一起看看,我在上图的特意标注的①边上的红色竖线。 红色竖线所处的时刻,是在发送读操作的第一个周期的命令 0x00之前的那一刻。 让我们看看,在那一刻,其所穿过好几行都对应什么值,以及进一步理解,为何要那个值。
1)红色竖线穿过的第一行,是 CLE。前面介绍命令所存使能(CLE)的那个引脚将CLE 置 1,就说明你将要通过 I/O 复用端口发送进入Nand Flash的,是命令,而不是地址或者其他类型的数据。只有这样将 CLE 置 1,使其有效,才能去通知了内部硬件逻辑,你接下来将收到的是命令,内部硬件逻辑,才会将受到的命令,放到命令寄存器中,才能实现后面正确的操作,否则,不去将 CLE 置 1 使其有效,硬件会无所适从,不知道你传入的到底是数据还是命令了。
2)而第二行,是 CE#,那一刻的值是 0。这个道理很简单,你既然要向Nand Flash发命令,那么先要选中它,所以,要保证 CE#为低电平,使其有效,也就是片选有效。
3)第三行是 WE#,意思是写使能。因为接下来是往 Nand Flash里面写命令,所以,要使
得 WE#有效,所以设为低电平。
4)第四行,是 ALE 是低电平,而 ALE 是高电平有效,此时意思就是使其无效。而对应地,前面介绍的,使 CLE 有效,因为将要数据的是命令(此时是发送图示所示的读命令第二周期的 0x30) ,而不是地址。如果在其他某些场合,比如接下来的要输入地址的时候,就要使其有效,而使 CLE 无效了。
5)第五行,RE#,此时是高电平,无效。可以看到,知道后面低 6 阶段,才变成低电平,才有效,因为那时候,要发生读取命令,去读取数据。
6)第六行,就是我们重点要介绍的,复用的输入输出 I/O 端口了,此刻,还没有输入数据,接下来,在不同的阶段,会输入或输出不同的数据/地址。
7)第七行,R/B#,高电平,表示 R(Ready)/就绪,因为到了后面的第 5阶段,硬件内部,在第四阶段,接受了外界的读取命令后,把该页的数据一点点送到页寄存器中,这段时间,属于系统在忙着干活,属于忙的阶段,所以,R/B#才变成低,表示 Busy忙的状态的。 其他的时序的就类似的理解。
8)以上资料摘字网络 经整理成为自己的东西。。。(I'm sorry I forgot to is the great god of the blog )
七、好了经过上面这么一分析再回到第五步 那些代码是否能看懂了<至少我明白了很多>
/***********************************************到此结束*******************************************/