全部博文(9)
2012年(9)
分类: LINUX
2012-10-22 13:56:49
1)u-boot版本1.1.6,gcc version 4.3.2
2)在Makefile中的smdk2410_config后加入
st2410_config : unconfig
@$(MKCONFIG) $(@:_config=) arm arm920t st2410 NULL s3c24x0我把我的板子起名叫st2410,可以依自己的喜好修改,注意@前用tab键空格。
各项的意思如下:
arm: CPU的架构(ARCH)
arm920t: CPU的类型(CPU),其对应于cpu/arm920t子目录。
st2410: 开发板的型号(BOARD),对应于board/st2410目录。
NULL: 开发者/或经销商(vender)。
s3c24x0: 片上系统(SOC)。
3)建立board/st2410目录,拷贝board/smdk2410下的文件到board/st2410目录,将smdk2410.c更名为st2410.c
cp -ar board/smdk2410/ board/st2410
mv board/st2410/smdk2410.c board/st2410/st2410.c
4)在board/st2410/Makefile中,如下修改(因为前面将smdk2410.c文件改名为st2410.c了):
COBJS:= smdk2410.o flash.o
改为:
COBJS:= st2410.o flash.o
5)cp include/configs/smdk2410.h include/configs/st2410.h
并将st2410.h中的
#define CFG_PROMPT "SMDK2410 # "
修改为
#define CFG_PROMPT "ST2410 # "
6)测试编译能否成功:
make st2410_config
make all ARCH=arm
生成u-boot.bin就OK了
支持Nandflash启动方式
1 修改cpu/arm920t/start.S文件
1)将以下代码
#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 */
修改为:
/***************** 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 *******************************/
/***************** CHECK_BOOT_FLASH *********************************/
#ifdef CONFIG_S3C2410
#define BWSCON 0x48000000
#endif
ldr r0, =BWSCON
ldr r0, [r0]
ands r0, r0, #6 /*OM[1:0] != 0, NOR FLash boot*/
bne relocate /*NOR FLash boot*/
/* adr r0, ResetEntry ;OM[1:0] == 0, NAND FLash boot
cmp r0, #0 ;if use Multi-ice,
bne copy_proc_beg ;don't read nand flash for boot*/
/***************** CHECK_BOOT_FLASH ************************************/
/***************** NAND_BOOT ********************************************/
#define LENGTH_UBOOT 0x60000
#define NAND_CTL_BASE 0x4E000000
#ifdef CONFIG_S3C2410
/* Offset */
#define oNFCONF 0x00
#define oNFCMD 0x04
#define oNFSTAT 0x10
@ reset NAND
mov r1, #NAND_CTL_BASE
ldr r2, =0xf830 @ initial value
str r2, [r1, #oNFCONF] /* 设置NFCONF寄存器 */
/* 设置NFCONT,初始化ECC编/解码器,禁止NAND Flash片选 */
ldr r2, [r1, #oNFCONF]
bic r2, r2, #0x800 @ enable chip
str r2, [r1, #oNFCONF]
mov r2, #0xff @ RESET command
strb r2, [r1, #oNFCMD] /* 复位命令,第一次使用NAND Flash前复位 */
mov r3, #0 @ wait
nand1:
add r3, r3, #0x1
cmp r3, #0xa
blt nand1
nand2:
ldr r2, [r1, #oNFSTAT] @ wait ready
tst r2, #0x1
beq nand2
ldr r2, [r1, #oNFCONF]
orr r2, r2, #0x800 @ disable chip
str r2, [r1, #oNFCONF]
@ get read to call C functions (for nand_read())
/* 为调用C函数nand_read_ll准备堆栈 */
ldr sp, DW_STACK_START @ setup stack pointer
mov fp, #0 @ no previous frame, so fp=0
/* 下面先设置r0至r2,然后调用nand_read_ll函数将U-Boot读入RAM */
@ copy U-Boot to RAM
ldr r0, =TEXT_BASE /* 目的地址:U-Boot在RAM的开始地址 */
mov r1, #0x0 /* 源地址:U-Boot在NAND Flash中的开始地址 */
mov r2, #LENGTH_UBOOT /* 复制的大小,必须比u-boot.bin文件大,并且必须是NAND Flash块大小的整数倍,这里设置为0x60000(KB) */
bl nand_read_ll /* 跳转到nand_read_ll函数,开始复制U-Boot到RAM */
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 **********************************************/
/***************** NOR_BOOT **********************************************/
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 */
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
/***************** NOR_BOOT ***********************************************/
2)在_start_armboot: .word start_armboot之后加入::
#define STACK_BASE 0x33f00000
#define STACK_SIZE 0x10000
.align 2
DW_STACK_START: .word STACK_BASE+STACK_SIZE-4
2在board/st2410加入NAND Flash读函数,建立nand_read.c
在上面添加的代码中有一个跳转:bl nand_read_ll ,它跳入是新增的 C 语言文件(board/st2410/nand_read.c)中的函数,这个文件原本是用 vivi 的代码,好来经过了 openmoko 的修改,并支持不同的 Nand Flash 芯片,我又多加了几个个芯片 ID以支持所有 2410 的 Nand Flash。代码如下:
/*
* 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
* Date : $Date: 2004/02/04 10:37:37 $
*
* u-boot integration and bad-block skipping (C) 2006 by OpenMoko, Inc.
* Author: Harald Welte
*/
#include
#include
#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
#if defined(CONFIG_S3C2410)
#define NFCONF __REGi(NF_BASE + 0x0)
#define NFCMD __REGb(NF_BASE + 0x4)
#define NFADDR __REGb(NF_BASE + 0x8)
#define NFDATA __REGb(NF_BASE + 0xc)
#define NFSTAT __REGb(NF_BASE + 0x10)
#define NFSTAT_BUSY 1
#define nand_select() (NFCONF &= ~0x800)
#define nand_deselect() (NFCONF |= 0x800)
#define nand_clear_RnB() do {} while (0)
#elif defined(CONFIG_S3C2440) || defined(CONFIG_S3C2442)
#define NFCONF __REGi(NF_BASE + 0x0)
#define NFCONT __REGi(NF_BASE + 0x4)
#define NFCMD __REGb(NF_BASE + 0x8)
#define NFADDR __REGb(NF_BASE + 0xc)
#define NFDATA __REGb(NF_BASE + 0x10)
#define NFDATA16 __REGw(NF_BASE + 0x10)
#define NFSTAT __REGb(NF_BASE + 0x20)
#define NFSTAT_BUSY 1
#define nand_select() (NFCONT &= ~(1 << 1))
#define nand_deselect() (NFCONT |= (1 << 1))
#define nand_clear_RnB() (NFSTAT |= (1 << 2))
#endif
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;
};
#if 0
#if defined(CONFIG_S3C2410) || defined(CONFIG_MINI2440)
/* configuration for 2410 with 512byte sized flash */
#define NAND_PAGE_SIZE 512
#define BAD_BLOCK_OFFSET 5
#define NAND_BLOCK_MASK (NAND_PAGE_SIZE - 1)
#define NAND_BLOCK_SIZE 0x4000
#else
/* configuration for 2440 with 2048byte sized flash */
#define NAND_5_ADDR_CYCLE
#define NAND_PAGE_SIZE 2048
#define BAD_BLOCK_OFFSET NAND_PAGE_SIZE
#define NAND_BLOCK_MASK (NAND_PAGE_SIZE - 1)
#define NAND_BLOCK_SIZE (NAND_PAGE_SIZE * 64)
#endif
/* compile time failure in case of an invalid configuration */
#if defined(CONFIG_S3C2410) && (NAND_PAGE_SIZE != 512)
#error "S3C2410 does not support nand page size != 512"
#endif
#endif
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();
#if defined(CONFIG_S3C2410)
for (i = 0; i < nand->page_size; i++) {
*buf = (NFDATA & 0xff);
buf++;
}
#elif defined(CONFIG_S3C2440) || defined(CONFIG_S3C2442)
for (i = 0; i < (nand->page_size>>1); i++) {
*ptr16 = NFDATA16;
ptr16++;
}
#endif
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;
}
NAND Flash根据page大小可分为2种: 512B/page和2048B/page的。但2410nandflash控制器仅支持512 B/page的,这里使用上面的函数处理,在2440的移植中再做更改。这两种NAND Flash的读操作是不同的。因此就需要U-Boot识别到NAND Flash的类型,然后采用相应的读操作,也就是说nand_read_ll函数要能自动适应两种NAND Flash。参考S3C2440的数据手册可以知道:根据NFCONF寄存器的Bit3(AdvFlash (Read only))和Bit2 (PageSize (Read only))可以判断NAND Flash的类型。Bit2、Bit3与NAND Flash的block类型的关系如下表所示:
表 2.4 NFCONF的Bit3、Bit2与NAND Flash的关系
由于的NAND Flash只有512B/page和2048 B/page这两种,因此根据NFCONF寄存器的Bit3即可区分这两种NAND Flash了。
完整代码见board/samsung/mini2440/nand_read.c中的nand_read_ll函数,这里给出伪代码:
int nand_read_ll(unsigned char *buf, unsigned long start_addr, int size)
{
//根据NFCONF寄存器的Bit3来区分2种NAND Flash
if( NFCONF & 0x8 ) /* Bit是1,表示是2KB/page的NAND Flash */
{
////////////////////////////////////
读取2K block 的NAND Flash
////////////////////////////////////
}
else /* Bit是0,表示是512B/page的NAND Flash */
{
/////////////////////////////////////
读取512B block 的NAND Flash
/////////////////////////////////////
}
return 0;
}
3 修改board/st2410/Makefile为
OBJS := st2410.o flash.o nand_read.o
4 在include/configs/st2410.h中添加
/*
* Nandflash Boot
*/
#define NAND_MAX_CHIPS 1
在nand_read.c文件中有用到。
5 重新编译u-boot
make all ARCH=arm
6 通过jtag将u-boot烧写到flash中就可以从NAND flash启动了
我的u-boot启动信息:
U-Boot 1.1.6 (Aug 8 2011 - 14:04:28)
DRAM: 64 MB
Flash: 512 kB
*** Warning - bad CRC, using default environment
In: serial
Out: serial
Err: serial
ST2410 #
可以看出:和第一次make的结果一样,u-boot命令依然不能用,也就是说不能用saveenv保存设置,因为我们现在只是完成了u-boot从NAND FLASH的启动工作,添加了nand_read.c函数,而不能实现写操作,
norflash启动时u-boot支持Nand Flash的读写操作
U-BOOT 1.1.5以后的版本对NAND FLASH的支持有新旧两套代码,心代码在drivers/mtd/nand目录下面,就代码在drivers/mtd/nand_legacy目录下面。文档 doc/README.nand对这两套代码有所说明:使用旧代码需要定义更多的宏,而新代码移植自Linux内核2.6.12,它更加智能,可以自动识别更多型号的NAND Flash。目前之所以还保留旧的代码,是因为两个目标板NETTA、NETTA_ISDN使用JFFS文件系统,它们还依赖于旧代码。当相关功能移植到新代码之后,旧的代码将从U-Boot中去除。这里我们移植用新的代码来支持nand flash.如果让U-BOOT支持 nand flash首先在include/configs/st2410.h里添加CONFIG_CMD_NAND宏,这些宏在incldue /config_cmd_all.h定义。然后选择哪套代码需要在include/configs/st2410.h定义 CONFIG_NAND_LEGACY宏,不定义则用新的代码。我们使用新的代码来支持nand flash。
现在开始移植:
要让U-Boot支持NAND Flash,首先在配置文件include/configs/st2410.h的宏CONFIG_COMMANDS中增加CFG_CMD_NAND,如下:
#define CONFIG_COMMANDS \
(CONFIG_CMD_DFL | \
CFG_CMD_CACHE | \
CFG_CMD_NAND | \
/*CFG_CMD_EEPROM |*/ \
/*CFG_CMD_I2C |*/ \
/*CFG_CMD_USB |*/ \
CFG_CMD_REGINFO | \
CFG_CMD_DATE | \
CFG_CMD_ELF)
……
然后选择使用哪套代码:在配置文件中定义宏CFG_NAND_LEGACY则使用旧代码,否则使用新代码。
使用旧代码时,需要实现drivers/nand_legacy/nand_legacy.c中使用到的各种宏,比如:
#define NAND_WAIT_READY(nand)/* 等待Nand Flash的状态为“就绪”,代码依赖于具体的开发板 */
#define WRITE_NAND_COMMAND(d, adr)/* 写NAND Flash命令,代码依赖于具体的开发板 */
代码的移植没有现成的文档,在CONFIG_COMMANDS中增加CFG_CMD_NAND后,就编译代码,然后一个一个地解决出现的错误。编译结果中出现的错误和警告如下:
from nand.c:28:
/home/just/study/uboot/nand-u-boot-1.1.6/include/linux/mtd/nand.h:412: error: 'NAND_MAX_CHIPS' undeclared here (not in a function)
nand.c:35: error: 'CFG_MAX_NAND_DEVICE' undeclared here (not in a function)
nand.c:38: error: 'CFG_NAND_BASE' undeclared here (not in a function)
make[1]: *** [nand.o] 错误 1
make[1]: Leaving directory `/home/just/study/uboot/nand-u-boot-1.1.6/drivers/nand'
make: *** [drivers/nand/libnand.a] 错误 2
在配置文件include/configs/st210.h中增加如下3个宏就可以解决上述错误。在Flash的驱动程序中,设备是逻辑上的概念,表示一组相同结构、访问函数相同的Flash芯片。在本书所用开发板中,只有一个NAND Flash芯片,所以设备数为1,芯片数也为1。
#define CFG_NAND_BASE 0x4e000000 /* nand flash控制器的基址,无实际意义,这在board_nand_init中重新指定 */
#define CFG_MAX_NAND_DEVICE 1/* NAND Flash“设备”的数目为1 */
#define NAND_MAX_CHIPS 1/* 每个NAND Flash“设备”由1个NAND Flash“芯片”组成 */
修改配置文件后再次编译,现在只有一个错误了,“board_nand_init函数未定义”:
nand.c:50: undefined reference to `board_nand_init'
调用board_nand_init函数的过程为:NAND Flash的初始化入口函数是nand_init,它在lib_arm/board.c的start_armboot函数中被调用;nand_init函数在drivers/nand/nand.c中实现,它调用相同文件中的nand_init_chip函数;nand_init_chip函数首先调用 board_nand_init函数来初始化NAND Flash设备,最后才是统一的识别过程。
从board_nand_init函数的名称就可以知道它是平台/开发板相关的函数,需要自己编写。本书在cpu/arm920t/s3c24x0 目录下新建一个文件nand_flash.c,在里面针对S3C2410、S3C2440实现了统一的board_nand_init函数。
在编写board_nand_init函数的之前,需要针对S3C2410、S3C2440 NAND Flash控制器的不同定义一些数据结构和函数:
(1) 在include/s3c24x0.h文件中增加S3C2440_NAND数据结构。S3C2410_NAND数据结构文件中已经有定义。
/* NAND FLASH (see S3C2440 manual chapter 6 ) */
typedef struct {
S3C24X0_REG32 NFCONF;
S3C24X0_REG32 NFCONT;
S3C24X0_REG32 NFCMD;
S3C24X0_REG32 NFADDR;
S3C24X0_REG32 NFDATA;
S3C24X0_REG32 NFMECCD0;
S3C24X0_REG32 NFMECCD1;
S3C24X0_REG32 NFSECCD;
S3C24X0_REG32 NFSTAT;
S3C24X0_REG32 NFESTAT0;
S3C24X0_REG32 NFESTAT1;
S3C24X0_REG32 NFMECC0;
S3C24X0_REG32 NFMECC1;
S3C24X0_REG32 NFSECC;
S3C24X0_REG32 NFSBLK;
S3C24X0_REG32 NFEBLK;
} /*__attribute__((__packed__))*/ S3C2440_NAND;
2)在include/s3c2410.h文件中仿照S3C2410_GetBase_NAND函数定义S3C2440_GetBase_NAND函数。
/* for s3c2440 */
static inline S3C2440_NAND * const S3C2440_GetBase_NAND(void)
{
return (S3C2440_NAND * const)S3C2410_NAND_BASE;
}
既然新的NAND Flash代码是从Linux内核2.6.12中移植来的,那么cpu/arm920t/s3c24x0/nand_flash.c文件也可以仿照内核中,对S3C2410、S3C2440的NAND Flash进行初始化的drivers/mtd/nand/s3c2410.c文件来编写。为了方便阅读,先把 cpu/arm920t/s3c24x0/nand_flash.c文件的代码全部列出来,再讲解:
/*
* s3c2410/s3c2440的NAND Flash控制器接口, 修改自Linux内核2.6.13文件drivers/mtd/nand/s3c2410.c
*/
#include
#if (CONFIG_COMMANDS & CFG_CMD_NAND) && !defined(CFG_NAND_LEGACY)
#include
#include
DECLARE_GLOBAL_DATA_PTR;
#define S3C2410_NFSTAT_READY (1<<0)
#define S3C2410_NFCONF_nFCE (1<<11)
#define S3C2440_NFSTAT_READY (1<<0)
#define S3C2440_NFCONT_nFCE (1<<1)
/* S3C2410:NAND Flash的片选函数 */
static void s3c2410_nand_select_chip(struct mtd_info *mtd, int chip)
{
S3C2410_NAND * const s3c2410nand = S3C2410_GetBase_NAND();
if (chip == -1) {
s3c2410nand->NFCONF |= S3C2410_NFCONF_nFCE;/* 禁止片选信号 */
} else {
s3c2410nand->NFCONF &= ~S3C2410_NFCONF_nFCE;/* 使能片选信号 */
}
}
/* S3C2410:命令和控制函数
* 注意,这个函数仅仅根据各种命令来修改“写地址”IO_ADDR_W 的值(这称为tglx方法),
* 这种方法使得平台/开发板相关的代码很简单。
* 真正发出命令是在上一层NAND Flash的统一的驱动中实现,
* 它首先调用这个函数修改“写地址”,然后才分别发出控制、地址、数据序列。
*/
static void s3c2410_nand_hwcontrol(struct mtd_info *mtd, int cmd)
{
S3C2410_NAND * const s3c2410nand = S3C2410_GetBase_NAND();
struct nand_chip *chip = mtd->priv;
switch (cmd) {
case NAND_CTL_SETNCE:
case NAND_CTL_CLRNCE:
printf("%s: called for NCE/n", __FUNCTION__);
break;
case NAND_CTL_SETCLE:
chip->IO_ADDR_W = (void *)&s3c2410nand->NFCMD;
break;
case NAND_CTL_SETALE:
chip->IO_ADDR_W = (void *)&s3c2410nand->NFADDR;
break;
/* NAND_CTL_CLRCLE: */
default:
chip->IO_ADDR_W = (void *)&s3c2410nand->NFDATA;
break;
}
}
/* S3C2410:查询NAND Flash状态
*
* 返回值:0 – 忙, 1 – 就绪
*/
static int s3c2410_nand_devready(struct mtd_info *mtd)
{
S3C2410_NAND * const s3c2410nand = S3C2410_GetBase_NAND();
return (s3c2410nand->NFSTAT & S3C2410_NFSTAT_READY);
}
/* S3C2440:NAND Flash的片选函数 */
static void s3c2440_nand_select_chip(struct mtd_info *mtd, int chip)
{
S3C2440_NAND * const s3c2440nand = S3C2440_GetBase_NAND();
if (chip == -1) {
s3c2440nand->NFCONT |= S3C2440_NFCONT_nFCE;/* 禁止片选信号 */
} else {
s3c2440nand->NFCONT &= ~S3C2440_NFCONT_nFCE;/* 使能片选信号 */
}
}
/* S3C2440:命令和控制函数,与s3c2410_nand_hwcontrol函数类似 */
static void s3c2440_nand_hwcontrol(struct mtd_info *mtd, int cmd)
{
S3C2440_NAND * const s3c2440nand = S3C2440_GetBase_NAND();
struct nand_chip *chip = mtd->priv;
switch (cmd) {
case NAND_CTL_SETNCE:
case NAND_CTL_CLRNCE:
printf("%s: called for NCE/n", __FUNCTION__);
break;
case NAND_CTL_SETCLE:
chip->IO_ADDR_W = (void *)&s3c2440nand->NFCMD;
break;
case NAND_CTL_SETALE:
chip->IO_ADDR_W = (void *)&s3c2440nand->NFADDR;
break;
/* NAND_CTL_CLRALE: */
default:
chip->IO_ADDR_W = (void *)&s3c2440nand->NFDATA;
break;
}
}
/* S3C2440:查询NAND Flash状态
* 返回值:0 – 忙, 1 – 就绪
*/
static int s3c2440_nand_devready(struct mtd_info *mtd)
{
S3C2440_NAND * const s3c2440nand = S3C2440_GetBase_NAND();
return (s3c2440nand->NFSTAT & S3C2440_NFSTAT_READY);
}
/*
* Nand flash硬件初始化:
* 设置NAND Flash的时序, 使能NAND Flash控制器
*/
static void s3c24x0_nand_inithw(void)
{
S3C2410_NAND * const s3c2410nand = S3C2410_GetBase_NAND();
S3C2440_NAND * const s3c2440nand = S3C2440_GetBase_NAND();
#define TACLS 0
#define TWRPH0 4
#define TWRPH1 2
if (gd->bd->bi_arch_number == MACH_TYPE_SMDK2410)
{
/* 使能NAND Flash控制器,初始化ECC,使能片选信号,设置时序 */
s3c2410nand->NFCONF = (1<<15)|(1<<12)|(1<<11)|(TACLS<<8)|(TWRPH0<<4)|(TWRPH1<<0);
}
else
{
/* 设置时序 */
s3c2440nand->NFCONF = (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4);
/* 初始化ECC,使能NAND Flash控制器,使能片选信号 */
s3c2440nand->NFCONT = (1<<4)|(0<<1)|(1<<0);
}
}
/*
* 被drivers/nand/nand.c调用, 初始化NAND Flash硬件,初始化访问接口函数
*/
void board_nand_init(struct nand_chip *chip)
{
S3C2410_NAND * const s3c2410nand = S3C2410_GetBase_NAND();
S3C2440_NAND * const s3c2440nand = S3C2440_GetBase_NAND();
s3c24x0_nand_inithw();/* Nand flash硬件初始化 */
if (gd->bd->bi_arch_number == MACH_TYPE_SMDK2410) {
chip->IO_ADDR_R = (void *)&s3c2410nand->NFDATA;
chip->IO_ADDR_W = (void *)&s3c2410nand->NFDATA;
chip->hwcontrol = s3c2410_nand_hwcontrol;
chip->dev_ready = s3c2410_nand_devready;
chip->select_chip = s3c2410_nand_select_chip;
chip->options = 0;/* 设置位宽等,位宽为8 */
} else {
chip->IO_ADDR_R = (void *)&s3c2440nand->NFDATA;
chip->IO_ADDR_W = (void *)&s3c2440nand->NFDATA;
chip->hwcontrol = s3c2440_nand_hwcontrol;
chip->dev_ready = s3c2440_nand_devready;
chip->select_chip = s3c2440_nand_select_chip;
chip->options = 0;/* 设置位宽等,位宽为8 */
}
chip->eccmode = NAND_ECC_SOFT;/* ECC较验方式:软件ECC */
}
#endif
文件中分别针对S3C2410、S3C2440实现了NAND Flash最底层访问函数,并进行了一些硬件的设置(比如时序、使能NAND Flash控制器等)。新的代码对NAND Flash的封装做得很好,只要向上提供底层初始化函数board_nand_init来设置好平台/开发板相关的初始化、提供底层接口即可。
最后,只要将新建的nand_flash.c文件编入U-Boot中就可以擦除、读写NAND Flash了。如下修改cpu/arm920t/s3c24x0/Makefile文件即可:
COBJS = i2c.o interrupts.o serial.o speed.o /
usb_ohci.o
改为:
COBJS = i2c.o interrupts.o serial.o speed.o /
usb_ohci.o nand_flash.o
加入 NAND 闪存芯片型号
在/include/linux/mtd/nand_ids.h 中对如下结构体赋值进行修改:
static struct nand_flash_dev nand_flash_ids[] = {
..........................................
{"Samsung K9F1208U0M", NAND_MFR_SAMSUNG, 0x76, 26, 0, 3, 0x4000, 0},
..........................................
}
这样对于该款 NAND 闪存芯片的操作才能正确执行。由于我移植的开发板上是K9F1208U0M,nand_ids.h 中有{"Samsung unknown 64Mb", NAND_MFR_SAMSUNG, 0x76, 26, 0, 3, 0x4000, 0},在这里可省出这一步。
现在,可以再编译,此时又出现下面错误
arm-linux-ld: failed to merge target specific data of file /opt/usr/local/arm/4.3.2/bin/../lib/gcc/arm-none-linux-gnueabi/4.3.2/armv4t/libgcc.a(_clz.o)
/opt/usr/local/arm/4.3.2/bin/../lib/gcc/arm-none-linux-gnueabi/4.3.2/armv4t/libgcc.a(_dvmd_lnx.o): In function `__aeabi_ldiv0':
(.text+0x8): undefined reference to `raise'
make: *** [u-boot] 错误 1
由于我用的toolchain版本是4.3.2,目前这个版本比较新,而我移植的uboot可是好几年前的版本了,所以它的“tricks”对该toolchain无能为力(或许新版的uboot能摆平)。
修改方法
将cpu/arm920t/config.mk 改为
PLATFORM_RELFLAGS += -fno-strict-aliasing -fno-common -ffixed-r8 \
-msoft-float
PLATFORM_CPPFLAGS +=
# ====================================================================
#
# Supply options according to compiler version
#
# ====================================================================
PLATFORM_CPPFLAGS +=$(call cc-option,)
PLATFORM_RELFLAGS +=$(call cc-option,$(call cc-option,))
在uboot的根目录Makefile文件中把PLATFORM_LIBS 修该为如下值
PLATFORM_LIBS += -L $(shell dirname `$(CC) $(CFLAGS) -print-libgcc-file-name`) -lgcc -lc -L/opt/usr/local/arm/4.3.2/arm-none-linux-gnueabi/libc/armv4t/usr/lib
然后重新
make distclean
make st2410_config
make all ARCH=arm
应就能通过了
我遇到问题就是在移植uboo1.1.6 中加入 CFG_CMD_NAND 就报那个错误 不过友善提供的可以通过
通过对比 修改了上面两个地方 就不在提示那个错误了
U-Boot 1.1.6 (Aug 9 2011 - 11:26:02)
DRAM: 64 MB
Flash: 512 kB
NAND: 64 MiB
*** Warning - bad CRC, using default environment
In: serial
Out: serial
Err: serial
ST2410 #
查看nand flash的信息:
ST2410 # nand info
Device 0: NAND 64MiB 3,3V 8-bit, sector size 16 KiB
擦出 nand:
ST2410 # nand erase 0x02200000 0x04000000
NAND erase: device 0 whole chip
Erasing at 0x2200000 -- 0
Erasing at 0x22a0000 -- 1
Erasing at 0x2344000 -- 2mplete.
Erasing at 0x23e8000 -- 3mplete.
Erasing at 0x248c000 -- 4mplete.
Erasing at 0x2530000 -- 5
NAND 64MiB 3,3V 8-bit: MTD Erase failure: -22
NAND 64MiB 3,3V 8-bit: MTD get bad block failed: -22
ERROR
现在的代码支持的非常好,能自动检测出坏快。不错!
*** Warning - bad CRC, using default environment
输入saveenv、reset保存不了变量。。。。
可能是没把新的环境变量保存到nandflash,原来uboot默认把环境变量保存到norflash中,这个也是在事后修改程序后才发觉的。修改st2410.h如下:
#define CFG_ENV_IS_IN_FLASH 1 /*enviroment is in the norflash*/这里为修改的。
改为:
//#define CFG_ENV_IS_IN_FLASH 1 /*enviroment is in the norflash*/
#define CFG_ENV_IS_IN_NAND 1
#define CFG_ENV_OFFSET 0x40000
修改完再生成u-boot.bin再烧入板中,在dnw下观察信息,竟然第一次的warning都没有,有点不可思议。
U-BOOT Nand命令支持
http://blogold.chinaunix.net/u1/47239/showart.php?id=376731
u-boot1.1.6 nand_legacy驱动提供了u-boot对nand相关命令的一个轻量级的实现,但好象可扩展性不足。本文主要分析u-boot 1.16/drivers/nand文件夹下的源程序。
一.关键数据结构
1.struct mtd_info
该结构在include\linux\mtd\Mtd.h中定义,字段比较多,有很多还是函数指针,它是MTD设备操作的通用接口,这个结构中有一个比较重要的成员 void *priv,priv被声明成void指针,在下文的分析中会知道priv实际上指向了nand_chip结构。
2.struct nand_chip
该结构在include\linux\mtd\Nand.h中定义,从名字上看就知道u-boot用它来描述Nand Flash芯片的结构,比如它定义了页地址的偏移,页地址的位掩码等。struct nand_chip不用我们手动的初始化,而是由另外一个结构,struct nand_flash_dev在程序中动态的初始化。
3.struct nand_flash_dev
该结构的定义有两处地方分别是
①include/linux/mtd/nand_legacy.h 由nand_legacy模块使用
②include/linux/mtd/nand.h 由u-boot通用nand架构使用
特别是在移植的时候要小心把两者混淆。我们先来看看改结构的定义
struct nand_flash_dev {
char *name;
int id;
unsigned long pagesize;
unsigned long chipsize;
unsigned long erasesize;
unsigned long options;
};
name : Nand Flash名称
id : u-boot内部id编号???
chipsize : 以MB为单位的芯片大小,比如64(M)
erasesize : 擦除块的大小,比如0x4000(16K)
options : 一些选项,比较重要的是Flash的数据位宽,如果你的Nand Flash是16位宽的,则必须包含NAND_BUSWIDTH_16选项。我们必须根据所使用的Nand Flash来填充里面的字段。
4.关键数据结构在程序中的使用
struct nand_info_t nand_info[ CFG_MAX_NAND_DEVICE ];
在 drivers\nand\nand.c中定义。CFG_MAX_NAND_DEVICE是板子的Nand Flash芯片的数量必须在板子的配置文件中定义(比如 include\configs\smdk2410.h)。
static struct nand_chip nand_chip[CFG_MAX_NAND_DEVICE];
在drivers\nand\nand.c中定义。CFG_MAX_NAND_DEVICE的定义同上。
struct nand_flash_dev nand_flash_ids[] = { … };
在drivers\nand\nand_ids.c中定义。这里要注意一点,在include\linux\mtd\nand_ids.h里面也nand_flash_ids[]的定义,那是由nand legacy驱动模块使用的。两者不能混淆!!!。在nand_flash_ids的定义中我找到了适合我的Nand Flash的结构描述:
{"NAND 64MiB 3,3V 8-bit", 0x76, 512, 64, 0x4000, 0}
设备ID为0x76,页大小为512Byte,总的容量为64M,擦除块为0x4000(16K),数据位宽8Bit。如果你的Nand Flash没有合适的描述,需要自己在该数组中添加相应的定义。
二.Nand Flash初始化
1.nand_init( drivers\nand\nand.c )
nand_init函数在lib_xxx/Board.c的start_armboot中调用。是u-boot Nand的主函数。nand_init的主要功能是对CFG_MAX_NAND_DEVICE个Nand设备进行初始化(调用nand_init_chip),累加Nand Flash的总大小。在nand_init结束时,可以配置是否执行board_nand_select_device,选择Nand芯片。
2.nand_init_chip( drivers\nand\nand.c )
static void nand_init_chip( struct mtd_info *mtd, struct nand_chip *nand, ulong base_addr )调用各个开发板提供的 board_nand_init 函数( board\
3.nand_scan ( drivers\nand\nand_base.c )
int nand_scan( struct mtd_info *mtd, int maxchips )
这是u-boot初始化nand设备的核心函数。它主要完成以下工作
1)初始化nand_chip的函数指针,这些函数一般在 board\
…
struct nand_chip *this = mtd->priv
....
if( this-> cmdfunc == NULL )
this->cmdfunc = nand_command;
…
上面是初始化nand_chip中cmdfunc指针的代码,如果在board_init_nand中开发板没有提供自己的nand_command函数,u-boot 将使用默认的nand_command函数(我觉得u-boot提供的这些默认的函数都不适合特定的硬件,所以很多都要自己重新写)。
2)使用上面注册的函数指针,读取Nand Flash的设备,并且在上文提到的nand_flash_ids[]中找是否有匹配项,若找到匹配的项,则初始化 nand_chip 和 mtd_info,它们的初始化代码老长的一段,一般没什么问题。
三. Nand Flash 操作
1. Read
函数调用层次:(如下图)。
以common/env_nand.c里面读取Nand Flash中的环境变量为例
common/env_nand.c
ret = nand_read( &nand_info[0], CFG_ENV_OFFSET, &total, (u_char*)env_ptr );
nand_info[]就是我们在1.4讲到的nand_info_t(mtd_info的别名)数组。此处的nand_read是个inline函数,下面是它的实现:
include/nand.h
static inline int nand_read(nand_info_t *info, ulong ofs, ulong *len, u_char *buf)
{
return info->read(info, ofs, *len, (size_t*)len, buf);
}
可以看出nand_read实际上调用的是nand_info的read方法。nand_info的read方法是在2.3中讲到的nand_scan中初始化
drivers/nand/nand_base.c
int nand_scan(struct mtd_info *mtd, int machips)
{
…
mtd->read = nand_read;
…
}
此处又一个nand_read!!!
drivers/nand/nand_base.c
static int nand_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf)
{
return nand_read_ecc( mtd, from, len, retlen, buf, NULL, NULL );
}
又一层包装!!!
drivers/nand/nand_base.c
static int nand_read_ecc(…)
{
…
}
终于到达最后一层了,nand_read_ecc通过调用nand_chip里面提供的函数对nand flash完成读的操作。具体可以看看代码,老长的一段。