1、修改include/configs/smdk2440.h
(1)添加Nand的定义:/*NAND flash settings*/
#define CONFIG_CMD_NAND
#if defined(CONFIG_CMD_NAND)
#define CONFIG_NAND_S3C2440
#define CONFIG_SYS_NAND_BASE 0x4E000000 //Nand配置寄存器基地址,nand.c要用到
#define CONFIG_SYS_MAX_NAND_DEVICE 1 //nand个数,nand.c要用到
#endif
2、修改arch/arm/include/asm/arch-s3c24x0/s3c24x0.h(CPU寄存器声明)
(1)添加2440Nand控制器寄存器声明
struct s3c2440_nand {
u32 NFCONF;
u32 NFCONT;
u32 NFCMMD;
u32 NFADDR;
u32 NFDATA;
u32 NFMECCD0;
u32 NFMECCD1;
u32 NFSECCD;
u32 NFSTAT;
u32 NFESTAT0;
u32 NFESTAT1;
u32 NFMECC0;
u32 NFMECC1;
u32 NFSECC;
u32 NFSBLK;
u32 NFEBLK;
};
3、在arch/arm/include/asm/arch-s3c24x0/目录中复制s3c2410.h为s3c2440.h,替换内容中s3c2410为s3c2440,替换内容中S3C2410为S3C2440(注意大小写)
4、修改arch/arm/include/asm/arch-s3c24x0/s3c24x0_cpu.h
(1)添加s3c2440.h头文件#ifdef CONFIG_S3C2400
#include <asm/arch/s3c2400.h>
#elif defined CONFIG_S3C2410
#include <asm/arch/s3c2410.h>
#elif defined CONFIG_S3C2440
#include <asm/arch/s3c2440.h>
#else
5、在drivers/mtd/nand目录中新增s3c2440_nand.c(参照s3c2410_nand.c修改)
(1)文件内容如下:#include <common.h>
#include <nand.h>
#include <asm/arch/s3c24x0_cpu.h>
#include <asm/io.h>
//NFCONF和NFCONT寄存器配置宏
#define S3C2440_NFCONF_TACLS(x) ((x)<<12)
#define S3C2440_NFCONF_TWRPH0(x) ((x)<<8)
#define S3C2440_NFCONF_TWRPH1(x) ((x)<<4)
#define S3C2440_NFCONT_SECCL (1<<6)
#define S3C2440_NFCONT_MECCL (1<<5)
#define S3C2440_NFCONT_INITECC (1<<4)
#define S3C2440_NFCONT_nCE (1<<1)
#define S3C2440_NFCONT_MODE (1<<0)
#define S3C2440_ADDR_NALE 0x08
#define S3C2440_ADDR_NCLE 0x0c
#ifdef CONFIG_NAND_SPL
/* in the early stage of NAND flash booting, printf() is not available */
#define printf(fmt, args...)
static void nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
{
int i;
struct nand_chip *this = mtd->priv;
for (i = 0; i < len; i++)
buf[i] = readb(this->IO_ADDR_R);
}
#endif
static void s3c2440_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
{
struct nand_chip *chip = mtd->priv;
struct s3c2440_nand *nand = s3c2440_get_base_nand(); //获取Nand控制器的寄存器结构体
debugX(1, "hwcontrol(): 0x%02x 0x%02x\n", cmd, ctrl);
if (ctrl & NAND_CTRL_CHANGE) {
ulong IO_ADDR_W = (ulong)nand; //NAND控制寄存器基地址
if (!(ctrl & NAND_CLE)) //要写的是地址
IO_ADDR_W |= S3C2440_ADDR_NCLE;
if (!(ctrl & NAND_ALE))//要写的是命令
IO_ADDR_W |= S3C2440_ADDR_NALE;
if (cmd == NAND_CMD_NONE)
IO_ADDR_W = &nand->NFDATA;//在写完命令和地址后,一定还要把IO端口的地址重新设置为寄存器NFDATA
chip->IO_ADDR_W = (void *)IO_ADDR_W;
if (ctrl & NAND_NCE)
writel(readl(&nand->NFCONF) & ~S3C2440_NFCONT_nCE,
&nand->NFCONF); //使能nand flash
else
writel(readl(&nand->NFCONF) | S3C2440_NFCONT_nCE,
&nand->NFCONF); //禁止nand flash
}
if (cmd != NAND_CMD_NONE)
writeb(cmd, chip->IO_ADDR_W);
}
static int s3c2440_dev_ready(struct mtd_info *mtd)
{
struct s3c2440_nand *nand = s3c2440_get_base_nand();
debugX(1, "dev_ready\n");
return readl(&nand->NFSTAT) & 0x01;
}
#ifdef CONFIG_S3C2440_NAND_HWECC
void s3c2410_nand_enable_hwecc(struct mtd_info *mtd, int mode)
{
struct s3c2440_nand *nand = s3c2440_get_base_nand();
debugX(1, "s3c2440_nand_enable_hwecc(%p, %d)\n", mtd, mode);
writel(readl(&nand->NFCONT) | S3C2440_NFCONT_INITECC, &nand->NFCONT);
}
static int s3c2440_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
u_char *ecc_code)
{
struct s3c2440_nand *nand = s3c2440_get_base_nand();
ecc_code[0] = readb(&nand->NFECC);
ecc_code[1] = readb(&nand->NFECC + 1);
ecc_code[2] = readb(&nand->NFECC + 2);
debugX(1, "s3c2440_nand_calculate_hwecc(%p,): 0x%02x 0x%02x 0x%02x\n",
mtd , ecc_code[0], ecc_code[1], ecc_code[2]);
return 0;
}
static int s3c2440_nand_correct_data(struct mtd_info *mtd, u_char *dat,
u_char *read_ecc, u_char *calc_ecc)
{
if (read_ecc[0] == calc_ecc[0] &&
read_ecc[1] == calc_ecc[1] &&
read_ecc[2] == calc_ecc[2])
return 0;
printf("s3c2440_nand_correct_data: not implemented\n");
return -1;
}
#endif
int board_nand_init(struct nand_chip *nand)
{
u_int32_t cfg;
u_int8_t tacls, twrph0, twrph1;
struct s3c24x0_clock_power *clk_power = s3c24x0_get_base_clock_power();
struct s3c2440_nand *nand_reg = s3c2440_get_base_nand();
debugX(1, "board_nand_init()\n");
writel(readl(&clk_power->CLKCON) | (1 << 4), &clk_power->CLKCON);
/* initialize hardware */
twrph0 = 2;
twrph1 = 1;
tacls = 0;
cfg = 0;
cfg |= S3C2440_NFCONF_TACLS(tacls);
cfg |= S3C2440_NFCONF_TWRPH0(twrph0);
cfg |= S3C2440_NFCONF_TWRPH1(twrph1);
writel(cfg, &nand_reg->NFCONF); //配置NFCONF
cfg = S3C2440_NFCONT_SECCL;
cfg |= S3C2440_NFCONT_MECCL;
cfg |= S3C2440_NFCONT_INITECC;
cfg |= S3C2440_NFCONT_MODE;
writel(cfg, &nand_reg->NFCONT); //配置NFCONT
/* initialize nand_chip data structure */
nand->IO_ADDR_R = nand->IO_ADDR_W = (void *)&nand_reg->NFDATA;
nand->select_chip = NULL;
/* read_buf and write_buf are default */
/* read_byte and write_byte are default */
#ifdef CONFIG_NAND_SPL
nand->read_buf = nand_read_buf;
#endif
/* hwcontrol always must be implemented */
nand->cmd_ctrl = s3c2440_hwcontrol;
nand->dev_ready = s3c2440_dev_ready;
#ifdef CONFIG_S3C2410_NAND_HWECC
nand->ecc.hwctl = s3c2440_nand_enable_hwecc;
nand->ecc.calculate = s3c2440_nand_calculate_ecc;
nand->ecc.correct = s3c2440_nand_correct_data;
nand->ecc.mode = NAND_ECC_HW;
nand->ecc.size = CONFIG_SYS_NAND_ECCSIZE;
nand->ecc.bytes = CONFIG_SYS_NAND_ECCBYTES;
#else
nand->ecc.mode = NAND_ECC_SOFT;
#endif
#ifdef CONFIG_S3C2440_NAND_BBT
nand->options = NAND_USE_FLASH_BBT;
#else
nand->options = 0;
#endif
debugX(1, "end of nand_init\n");
return 0;
}
6、修改drivers/mtd/nand/目录下Makefile
(1)添加编译选项:COBJS-$(CONFIG_NAND_S3C2410) += s3c2410_nand.o
COBJS-$(CONFIG_NAND_S3C2440) += s3c2440_nand.o
COBJS-$(CONFIG_NAND_S3C64XX) += s3c64xx.o
7、修改include/configs/smdk2440.h
(1)定义环境变量存放位置的宏
#define CONFIG_ENV_IS_IN_NAND 1 //common目录Makefile,连接时要用
8、修改arch/arm/cpu/arm920t/start.S
(1)判断是Nor启动还是Nand启动,在relocate:/*relocate U-Boot to RAM*/后面添加:/***************** CHECK_BOOT_FLASH **************************************/
ldr r1, =0x4000003C/* address of Internal SRAM 0x4000003C*/
mov r0, #0 /* r0 = 0 */
str r0, [r1]
mov r1, #0x3c /* address of 0x0000003C*/
ldr r0, [r1]
cmp r0, #0 /*判断0x0000003C所指向的4个字节是否被清0*/
bne norboot /*没有被清0,就是在Nor Flash中启动,则跳到Nor Flash的启动代码处norboot*/
/***************** CHECK_BOOT_FLASH *********************************/
/***************** NAND_BOOT *************************************************/
nandboot:
/* recovery */
ldr r0, =0xdeadbeef /*被清0,就是在Nand Flash中启动*/
ldr r1, =0x4000003C /*恢复0x4000003C指向的4字节的数据0xdeadbeef*/
str r0, [r1]
#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, =( (2<<12)|(1<<8)|(0<<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 *************************************************/
/***************** NOR_BOOT *************************************************/
norboot:
/*********** CHECK_FOR_MAGIC_NUMBER***************/
ldr r1, =0xdeadbeef
cmp r0, r1
bne loop3
/*********** CHECK_FOR_MAGIC_NUMBER***************/
(2)添加nandboot中DW_STACK_START的定义:
_start_armboot: .word start_armboot
#define STACK_BASE 0x33f00000
#define STACK_SIZE 0x10000
.align 2
DW_STACK_START: .wordSTACK_BASE+STACK_SIZE-4
9、在board/samsung/smdk2440文件夹中创建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)
#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))
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;
}
10、修改board/samsung/smdk2440目录下Makefile文件,加入对它的编译选项支持
COBJS :=smdk2440.o flash.o nand_read.o
11、修改arch/arm/cpu/arm920t/u-boot.lds文件
(1)添加nand_read.o到4K地址之内
arch/arm/cpu/arm920t/start.o (.text)
board/samsung/smdk2440/lowlevel_init.o (.text)
board/samsung/smdk2440/nand_read.o (.text)