Mini2440之uboot移植之实践NAND启动(四)
在之前的章节我们已经介绍了u-boot如何支持我们的NOR FLASH,以及DM9000网卡的支持。
这一节我们将会在Young / s3c2440_project[u-boot-2016.05-nor-flash】代码的基础上新建u-boot-2016.05-nand-flash项目,使得我们项目支持NAND,由于u-boot默认采用的NOR启动,这里我们需要修改u-boot实现NAND启动。
一、NAND启动
如果想支持NAND启动,我们需要考虑以下问题,S3C2440在NAND启动时S3C2440的NAND 控制器会自动把NAND FLASH中的前4K代码数据搬到内部SRAM中(在S3C2440片内有一块被称为SteppingStone的片内SRAM,它的大小为4K),片内SRAM被映射到nGCS0片选的空间(nGCS0片选的空间,即0x00000000),CPU从0x00000000位置开始运行程序。
然而我们的uboot程序是远远大于4kb的,因此程序大于4kb的时候需要从 NAND FLASH中拷贝到 SDRAM中去运行。自然可以想到烧到 NAND FLASH中的程序前面一部分代码应该是初始化SDRAM(程序最终需要拷贝到SDRAM中去运行)和将NAND FLASH中的剩余的程序拷贝到SDRAM中去(全考过去也行,方便点),然后跳转到SDRAM中执行。
既然我们已经有了思路,那么接下来就是如何去实现。
在分析Mini440之uboot移植之源码分析start.S(一)中我们介绍到CPU上点首先执行的是start.S文件(arch/arm/cpu/arm920t),在内核级配置和初始化过程中会调用cpu_init_crit函数。
我们可以在cpu_init_crit函数执行完之后进行适当扩充,添加以下功能:
- NAND初始化;
- 代码重定位,将uboot拷贝到SDRAM,并进行跳转运行;
- 屏蔽u-boot原生重定位代码;
1.1 新建init.c文件
新建init.c文件并将文件放在board/samsung/smdk2440路径下,并修改当前路径下的Makefile:
obj-y := smdk2440.o init.o
obj-y += lowlevel_init.o
init.c代码如下:
/************************************************************************** * * FileName : init.c * Function : 一些初始化相关的函数 * Author : zy * *************************************************************************/ #include <init.h> /************************************************************************** * * Function : nand flash读写相关函数 * *************************************************************************/ /* 等待NAND Flash就绪 */ void _nand_wait_idle(void) { int i; while(!(NFSTAT & BUSY)); for(i=0; i<10; i++); } /* 发出片选信号 */ void _nand_select_chip(void) { int i; NFCONT &= ~(1<<1); /* set CE=0 */ for(i=0; i<10; i++); } /* 取消片选信号 */ void _nand_deselect_chip(void) { NFCONT |= (1<<1); /* set CE=1 */ } /* 发出命令 */ void _nand_write_cmd(unsigned char cmd) { int i; NFCMMD = cmd; for(i=0; i<10; i++); } /* 发出地址 */ void _nand_write_addr_byte(unsigned char addr) { volatile int i; NFADDR = addr; for(i=0; i<10; i++); } /* 发出地址,5个周期来发送,前2个周期为col地址,后三个周期为row(page)地址 */ void _nand_write_addr(unsigned int addr) { int col, page; col = addr & NAND_PAGE_MASK_SIZE; page = addr / NAND_PAGE_SIZE; /* 块号 */ _nand_write_addr_byte(col & 0xff); /* Column Address A0~A7 */ _nand_write_addr_byte((col >> 8) & 0x0f); /* Column Address A8~A11 */ _nand_write_addr_byte(page & 0xff); /* Row Address A12~A19 */ _nand_write_addr_byte((page >> 8) & 0xff); /* Row Address A20~A27 */ _nand_write_addr_byte((page >> 16) & 0x01); /* Row Address A28 */ } /* 复位 */ void _nand_reset(void) { _nand_select_chip(); _nand_write_cmd(0xff); // 复位命令 _nand_wait_idle(); _nand_deselect_chip(); } /* 读取数据 */ unsigned char _nand_read_data(void) { return NFDATA; } /* 写入数据 */ void _nand_write_data(unsigned char data) { int i; NFDATA = data; for(i=0; i<10; i++); } /* 初始化and Flash */ void nand_init_ll(void) { #define TACLS 0 #define TWRPH0 1 #define TWRPH1 0 /* 设置时序 */ NFCONF = (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4); /* 使能NAND Flash控制器, 初始化ECC, 禁止片选, 使能NAND控制器 */ NFCONT = (1<<4)|(1<<1)|(1<<0); /* 复位NAND Flash */ _nand_reset(); } /* 发送读取数据命令,将nand start_addr处数据读取到buf中,读取长度为size, start_addr参数必须按页对齐 success:0 fail:-1 */ int nand_read_data(unsigned char *buf, unsigned int start_addr, unsigned int size) { int i = 0; int j = 0; if (start_addr & NAND_PAGE_MASK_SIZE ) { return -1; /* 地址或长度不对齐 */ } /* 选中芯片 */ _nand_select_chip(); /* 按页读取 */ while (i < size) { /* 发出READ命令 */ _nand_write_cmd(0); /* write address */ _nand_write_addr(start_addr+i); /* write cmd */ _nand_write_cmd(0x30); /* wait */ _nand_wait_idle(); /* 读取一页数据 */ for(; (j < NAND_PAGE_SIZE) && (i < size); j++) { buf[i++] = _nand_read_data(); } j = 0; } /* 取消片选信号 */ _nand_deselect_chip(); return 0; } /************************************************************************** * * Function : 自动区分是nand启动还是nor启动 * NAND启动,此时内部4k SRAM映射到0x00处,才可以访问该内存 * NOR启动,此时内部2M NOR FLASH映射到地址0x00处,此时无法写入内存,片内SRAM映射到0x40000000地址处 * *************************************************************************/ int is_boot_from_nor_flash(void) { volatile unsigned int *p = (volatile unsigned int *)0x00; unsigned int val = *p; /* get value from address 0x00 */ *p = 0x12345678; /* write value to address 0x00 */ /* 写成功, 对应nand启动 */ if(*p == 0x12345678) { *p = val; return 0; } return 1; } /************************************************************************** * * Function : 清除bss段 * *************************************************************************/ void clear_bss(void) { /* 要从lds文件中获得 __bss_start, __bss_end */ extern int __bss_end, __bss_start; int *p = &__bss_start; for (; p < &__bss_end; p++) *p = 0; } /************************************************************************** * * Function : 将代码从nand/nor falsh复制到sdram * *************************************************************************/ void copy_code_to_sdram(void) { /* 要从lds文件中获得 __image_copy_start, __image_copy_end 然后从0地址把数据复制到__image_copy_start */ extern int __image_copy_start, __image_copy_end; volatile unsigned int *dest = (volatile unsigned int *)&__image_copy_start; volatile unsigned int *end = (volatile unsigned int *)&__image_copy_end; volatile unsigned int *src = (volatile unsigned int *)0; unsigned int len = (unsigned int)(&__image_copy_end) - (unsigned int)(&__image_copy_start); /* nor falsh boot: nor flash address 0x00 */ if (is_boot_from_nor_flash()) { /* 把nor flash的内容全部copy到sdram */ while (dest < end) { *dest++ = *src++; } } else { // 将nand flash内容复制到SDRAM nand_init_ll(); nand_read_data(dest, (unsigned int)src, len); } }
在include目录下新建init.h头文件,代码如下:
#ifndef __S3C2440_INIT__ #define __S3C2440_INIT__ #define GSTATUS1 (*(volatile unsigned long *)0x560000B0) #define BUSY 1 /* page size 2048byte */ #define NAND_PAGE_SIZE 2048 #define NAND_PAGE_MASK_SIZE (NAND_PAGE_SIZE - 1) #define NAND_BLOCK_SIZE (64*NAND_PAGE_SIZE) #define NAND_BLOCK_MASK_SIZE (NAND_BLOCK_SIZE-1) /* NAND FLASH (see S3C2440 manual chapter 6, www.100ask.net) */ #define NFCONF (*(volatile unsigned int *)0x4e000000) // 配置寄存器 #define NFCONT (*(volatile unsigned int *)0x4e000004) // 控制寄存器 #define NFCMMD (*(volatile unsigned char *)0x4e000008) // 命令寄存器 #define NFADDR (*(volatile unsigned char *)0x4e00000C) // 地址寄存器 #define NFDATA (*(volatile unsigned char *)0x4e000010) // 数据寄存器 #define NFMECCD0 (*(volatile unsigned int *)0x4e000014) #define NFMECCD (*(volatile unsigned int *)0x4e000018) #define NFSECCD (*(volatile unsigned int *)0x4e00001C) #define NFSTAT (*(volatile unsigned int *)0x4e000020) #define NFESTAT0 (*(volatile unsigned int *)0x4e000024) #define NFESTAT1 (*(volatile unsigned int *)0x4e000028) #define NFMECC0 (*(volatile unsigned int *)0x4e00002C) #define NFMECC1 (*(volatile unsigned int *)0x4e000030) #define NFSECC (*(volatile unsigned int *)0x4e000034) #define NFSBLK (*(volatile unsigned int *)0x4e000038) #define NFEBLK (*(volatile unsigned int *)0x4e00003C) /* 函数声明 */ extern void copy_code_to_sdram(); extern void clear_bss(void); #endif
修改include/configs/smdk2440.h文件,将CONFIG_SYS_TEXT_BASE宏改为0x33f00000,也就是uboot重定位后的位置, 这里没有设置成0x33f80000,而是留了1M空间供给uboot重定位,主要是因为我们的u-boot可能大于512kb。
具体可以看考Mini440之uboot移植之源码分析u-boot重定位(三):
1.2 start.S修改(arch/arm/cpu/arm920t/start.S)
lowlevel_init函数执行完之后就是执行bl _main(arch/arm/lib/crt0.S),因此我们可以在cpu_init_crit执行之后,并加入NAND初始化,以及重定位相关代码。修改之后的start.S代码:
#ifndef CONFIG_SKIP_LOWLEVEL_INIT bl cpu_init_crit #endif ldr sp, =(CONFIG_SYS_INIT_SP_ADDR) @ 设置栈 0x30000f50 bic sp, sp, #7 /* 8-byte alignment for ABI compliance */ bl copy_code_to_sdram @ 代码重定位 bl clear_bss @ 清除bss段 ldr pc,=_main @ 绝对跳转,跳转到SDRAM运行
最后我们使用ldr进行了绝对跳转,跳转到SDRAM中运行。
1.3 屏蔽u-boot重定位代码
因为我们已经将代码从NAND FLASH拷贝到了SDRAM中,因此需要屏蔽掉u-boot的重定位、以及BSS清除相关的代码。屏蔽掉_main(arch/arm/lib/crt0.S)中如下代码:
ldr r0, [r9, #GD_RELOCADDR] /* r0 = gd->relocaddr */
b relocate_code
修改reserve_uboot(common/board_f.c):
static int reserve_uboot(void) { /* * reserve memory for U-Boot code, data & bss * round down to next 4 kB limit */ gd->relocaddr = CONFIG_SYS_TEXT_BASE; debug("Reserving %ldk for U-Boot at: %08lx\n", gd->mon_len >> 10, gd->relocaddr); gd->start_addr_sp = gd->relocaddr; return 0; }
1.4 修改链接脚本
start.S, init.c(实现重定位), lowlevel.S(实现初始化SDRAM)等文件放在内存空间最前面。修改arch/arm/cpu/u-boot.lds部分信息如下:
.text : { *(.__image_copy_start) *(.vectors) CPUDIR/start.o (.text*) board/samsung/smdk2440/built-in.o (.text*) *(.text*) }
build-in.o是将smdk2440单板目录下的所有*.c,*S文件编译后,连接成一个库文件。
1.5 去掉 "-pie"选项
在u-boot源码分析重定位中,我们说过在arm-linux-ld时加了"-pie"选项, 使得u-boot.bin里多了"*(.rel*)", "*(.dynsym)",从而程序非常大,不利于从NAND启动,所以我们修改代码,并取消"-pie"选项。运行:
grep "\-pie" * -nR
我们修改arch/arm/config.mk,屏蔽掉下面代码:
LDFLAGS_u-boot += -pie
此外,我们还需要屏蔽掉顶层Makefile文件中的重定位规则检验的代码,否则编译汇报如下错误:
我们定位到1387行,修改为如下:
checkarmreloc: u-boot @RELOC="`$(CROSS_COMPILE)readelf -r -W $< | cut -d ' ' -f 4 | \ grep R_A | sort -u`"; \ # if test "$$RELOC" != "R_ARM_RELATIVE" -a \ # "$$RELOC" != "R_AARCH64_RELATIVE"; then \ # echo "$< contains unexpected relocations: $$RELOC"; \ # false; \ # fi
修改arch/arm/cpu/u-boot.lds,将如下内容:
.bss_start __rel_dyn_start (OVERLAY) : { KEEP(*(.__bss_start)); __bss_base = .; } .bss __bss_base (OVERLAY) : { *(.bss*) . = ALIGN(4); __bss_limit = .; } .bss_end __bss_limit (OVERLAY) : { KEEP(*(.__bss_end)); }
修改为:
. = ALIGN(4); /* 定义变量,保存.bss段起始地址,可以在汇编代码中之间使用 */ __bss_start = .; /* 全局的未初始化变量存在于.bss段中 */ .bss : { *(.bss*) } __bss_end = .;
为什么修改这个呢,主要是因为源代码配置的bss和rel.dyn使用的同一个段空间,而我们编译时取消了-pie选项,会导致bss段起始地址变成0x00。具体可以参考u-boot中bss段的使用。
二、NAND识别
u-boot在启动过程中,同样是识别不了NAND的,这一小节,我们将修改代码,使其能够识别NAND,并正常输出其大小。
2.1 添加NAND FLASH操作文件
拷贝drivers/mtd/nand/s3c2410_nand.c为s4c2440_nand.c到同目录下。
cd drivers/mtd/nand cp s3c2410_nand.c s3c2440_nand.c
修改当前目录下的Makefile如下:
2.2 分析nand初始化调用流程
在Mini440之uboot移植之源码分析board_init_r(四)我们介绍到board_init_r()函数会进行nand的初始化。具体在 initr_nand()函数执行过程中,该函数位于common/board_r.c文件:
static int initr_nand(void) { puts("NAND: "); nand_init(); return 0; }
初始化nand flash,并输出nand falsh信息。其中nand_init()定义在drivers/mtd/nand/nand.c文件中,去掉无用代码如下:
void nand_init(void) { int i; for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++) nand_init_chip(i); printf("%lu MiB\n", total_nand_size / 1024); }
nand_init_chip()函数代码如下(driver/mtd/nand/nand.c文件中):
static void nand_init_chip(int i) { struct mtd_info *mtd = &nand_info[i]; struct nand_chip *nand = &nand_chip[i]; ulong base_addr = base_address[i]; int maxchips = CONFIG_SYS_NAND_MAX_CHIPS; if (maxchips < 1) maxchips = 1; mtd->priv = nand; nand->IO_ADDR_R = nand->IO_ADDR_W = (void __iomem *)base_addr; if (board_nand_init(nand)) return; if (nand_scan(mtd, maxchips)) return; nand_register(i); }
其中board_nand_init()函数与nand_scan()函数最终都会调用drivers/mtd/nand/s3c2440_nand.c里的函数,所以需要修改s3c2440_nand.c文件来支持我们的NAND FLASH。
2.3 修改s3c2440_nand.c
1. 将该文件所有的2410都替换为2440;
2. 添加s3c24x0_nand_select()函数;
在board_nand_init()函数前添加s3c24x0_nand_select()函数,函数代码如下:
static void s3c24x0_nand_select(struct mtd_info *mtd, int chipnr) { struct s3c24x0_nand *nand = s3c24x0_get_base_nand(); switch (chipnr) { case -1: //取消选择 nand->nfcont |= (1<<1); break; case 0: /*选中*/ nand->nfcont&=~(1<<1); break; default: BUG(); } }
3. 修改board_nand_init()函数:
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 s3c24x0_nand *nand_reg = s3c24x0_get_base_nand(); debug("board_nand_init()\n"); writel(readl(&clk_power->clkcon) | (1 << 4), &clk_power->clkcon); /* initialize hardware */ #if defined(CONFIG_S3C24XX_CUSTOM_NAND_TIMING) tacls = CONFIG_S3C24XX_TACLS; twrph0 = CONFIG_S3C24XX_TWRPH0; twrph1 = CONFIG_S3C24XX_TWRPH1; #else tacls = 4; twrph0 = 8; twrph1 = 8; #endif cfg = S3C2440_NFCONF_TACLS(tacls - 1); cfg |= S3C2440_NFCONF_TWRPH0(twrph0 - 1); cfg |= S3C2440_NFCONF_TWRPH1(twrph1 - 1); writel(cfg, &nand_reg->nfconf); // 设置时序 /* 使能NAND Flash控制器, 初始化ECC, 禁止片选 */ writel((1<<4)|(1<<1)|(1<<0), &nand_reg->nfcont); /* initialize nand_chip data structure */ nand->IO_ADDR_R = (void *)&nand_reg->nfdata; nand->IO_ADDR_W = (void *)&nand_reg->nfdata; nand->select_chip = s3c24x0_nand_select; // 发出片选信息、取消片选信号 /* 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 = s3c24x0_hwcontrol; // 发出命令 或者发出地址 nand->dev_ready = s3c24x0_dev_ready; #ifdef CONFIG_S3C2440_NAND_HWECC nand->ecc.hwctl = s3c24x0_nand_enable_hwecc; nand->ecc.calculate = s3c24x0_nand_calculate_ecc; nand->ecc.correct = s3c24x0_nand_correct_data; nand->ecc.mode = NAND_ECC_HW; nand->ecc.size = CONFIG_SYS_NAND_ECCSIZE; nand->ecc.bytes = CONFIG_SYS_NAND_ECCBYTES; nand->ecc.strength = 1; #else nand->ecc.mode = NAND_ECC_SOFT; // 软件ecc校验 #endif #ifdef CONFIG_S3C2440_NAND_BBT nand->bbt_options |= NAND_BBT_USE_FLASH; #endif debug("end of nand_init\n"); return 0; }
修改宏定义:
#define S3C2440_NFCONT_INITECC (1<<4) #define S3C2440_NFCONF_TACLS(x) ((x)<<12) #define S3C2440_NFCONF_TWRPH0(x) ((x)<<8) #define S3C2440_NFCONF_TWRPH1(x) ((x)<<4) // 地址寄存器偏移 #define S3C2440_ADDR_NALE 0x0C // 命令寄存器偏移 #define S3C2440_ADDR_NCLE 0x08
4.修改s3c2440_hwcontrol区分命令和地址
static void s3c24x0_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl) { struct nand_chip *chip = mtd->priv; struct s3c24x0_nand *nand = s3c24x0_get_base_nand(); debug("hwcontrol(): 0x%02x 0x%02x\n", cmd, ctrl); if (ctrl & NAND_CTRL_CHANGE) { ulong IO_ADDR_W = (ulong)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; chip->IO_ADDR_W = (void *)IO_ADDR_W; } if (cmd != NAND_CMD_NONE) writeb(cmd, chip->IO_ADDR_W); }
在这个函数中,除了修改寄存器的值以及设置写命令和写地址的IO端口外,我们还增加了if(cmd == NAND_CMD_NONE)判断语句。
如果不加这个判断语句,向NAND FLASH内写数据是写不进去的,尽管系统不会提示任何错误,并显示“OK”,但其实数据是没有被写入的,因此一定要加上这条语句。
这是因为在写完命令和地址后,一定还要把IO端口的地址重新设置为寄存器NFDATA。
需要说明的是,由于系统没有定义CONFIG_S3C2440_NAND_HWECC,因此我们暂时先不对s3c24x0_nand_enable_hwecc函数、s3c24x0_nand_calculate_ecc函数和s3c24x0_nand_correct_data函数进行修改。
完整代码:
/* * (C) Copyright 2006 OpenMoko, Inc. * Author: Harald Welte <laforge@openmoko.org> * * SPDX-License-Identifier: GPL-2.0+ */ #include <common.h> #include <nand.h> #include <asm/arch/s3c24x0_cpu.h> #include <asm/io.h> #define S3C2440_NFCONT_INITECC (1<<4) #define S3C2440_NFCONF_TACLS(x) ((x)<<12) #define S3C2440_NFCONF_TWRPH0(x) ((x)<<8) #define S3C2440_NFCONF_TWRPH1(x) ((x)<<4) // 地址寄存器偏移 #define S3C2440_ADDR_NALE 0x0C // 命令寄存器偏移 #define S3C2440_ADDR_NCLE 0x08 #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 s3c24x0_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl) { struct nand_chip *chip = mtd->priv; struct s3c24x0_nand *nand = s3c24x0_get_base_nand(); debug("hwcontrol(): 0x%02x 0x%02x\n", cmd, ctrl); if (ctrl & NAND_CTRL_CHANGE) { ulong IO_ADDR_W = (ulong)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; chip->IO_ADDR_W = (void *)IO_ADDR_W; } if (cmd != NAND_CMD_NONE) writeb(cmd, chip->IO_ADDR_W); } static int s3c24x0_dev_ready(struct mtd_info *mtd) { struct s3c24x0_nand *nand = s3c24x0_get_base_nand(); debug("dev_ready\n"); return readl(&nand->nfstat) & 0x01; } #ifdef CONFIG_S3C2440_NAND_HWECC void s3c24x0_nand_enable_hwecc(struct mtd_info *mtd, int mode) { struct s3c24x0_nand *nand = s3c24x0_get_base_nand(); debug("s3c24x0_nand_enable_hwecc(%p, %d)\n", mtd, mode); writel(readl(&nand->nfconf) | S3C2440_NFCONT_INITECC, &nand->nfconf); } static int s3c24x0_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code) { struct s3c24x0_nand *nand = s3c24x0_get_base_nand(); ecc_code[0] = readb(&nand->nfecc); ecc_code[1] = readb(&nand->nfecc + 1); ecc_code[2] = readb(&nand->nfecc + 2); debug("s3c24x0_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 s3c24x0_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("s3c24x0_nand_correct_data: not implemented\n"); return -1; } #endif static void s3c24x0_nand_select(struct mtd_info *mtd, int chipnr) { struct s3c24x0_nand *nand = s3c24x0_get_base_nand(); switch (chipnr) { case -1: //取消选择 nand->nfcont |= (1<<1); break; case 0: /*选中*/ nand->nfcont&=~(1<<1); break; default: BUG(); } } 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 s3c24x0_nand *nand_reg = s3c24x0_get_base_nand(); debug("board_nand_init()\n"); writel(readl(&clk_power->clkcon) | (1 << 4), &clk_power->clkcon); /* initialize hardware */ #if defined(CONFIG_S3C24XX_CUSTOM_NAND_TIMING) tacls = CONFIG_S3C24XX_TACLS; twrph0 = CONFIG_S3C24XX_TWRPH0; twrph1 = CONFIG_S3C24XX_TWRPH1; #else tacls = 4; twrph0 = 8; twrph1 = 8; #endif cfg = S3C2440_NFCONF_TACLS(tacls - 1); cfg |= S3C2440_NFCONF_TWRPH0(twrph0 - 1); cfg |= S3C2440_NFCONF_TWRPH1(twrph1 - 1); writel(cfg, &nand_reg->nfconf); /* 使能NAND Flash控制器, 初始化ECC, 禁止片选 */ writel((1<<4)|(1<<1)|(1<<0), &nand_reg->nfcont); /* initialize nand_chip data structure */ nand->IO_ADDR_R = (void *)&nand_reg->nfdata; nand->IO_ADDR_W = (void *)&nand_reg->nfdata; nand->select_chip = s3c24x0_nand_select; /* 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 = s3c24x0_hwcontrol; nand->dev_ready = s3c24x0_dev_ready; #ifdef CONFIG_S3C2440_NAND_HWECC nand->ecc.hwctl = s3c24x0_nand_enable_hwecc; nand->ecc.calculate = s3c24x0_nand_calculate_ecc; nand->ecc.correct = s3c24x0_nand_correct_data; nand->ecc.mode = NAND_ECC_HW; nand->ecc.size = CONFIG_SYS_NAND_ECCSIZE; nand->ecc.bytes = CONFIG_SYS_NAND_ECCBYTES; nand->ecc.strength = 1; #else nand->ecc.mode = NAND_ECC_SOFT; #endif #ifdef CONFIG_S3C2440_NAND_BBT nand->bbt_options |= NAND_BBT_USE_FLASH; #endif debug("end of nand_init\n"); return 0; }
2.4 硬件ECC支持
在没有开启CONFIG_S3C2440_NAND_HWECC配置的情况下,对NAND读写操作进行的都是软件ECC校验,即用软件编程的方法实现ECC。
如果想要实现硬件ECC、我们需要配置CONFIG_S3C2440_NAND_HWECC,并对代码进行修改,具体参考u-boot-2016.03 在mini2440上移植之nandflash 硬件ecc,这里就不介绍了。
三、 编译下载运行
3.1 配置编译
make distclean make smdk2440_defconfig make ARCH=arm CROSS_COMPILE=arm-linux- V=1
3.2 查看内存分布
直接反汇编u-boot文件,可以得到反汇编代码:
arm-linux-objdump -D u-boot > u-boot.dis
查看u-boot.dis,可以看到代码的链接起始地址为0x33f00000:
继续查看u-boot.dis,找到NAND初始化代码:
可以看到NAND相关代码均没有超过4kb,执行完代码重定位后,执行了代码跳转,跳转到SDRAM中运行__main函数:
3.3 下载到NAND FALSH
我们将代码下载到NAND FLASH中,并从NAND启动。这里有两种方式下载到NAND:
- 使用MiniTools下载程序,我这u-boot后编译还有400kb+,MiniTools无法下载成功,好像是MiniTools不支持下载大于256KB的u-boot。
- 使用网络下载,由于我这台开发板网卡坏掉了没发通过tftp下载到NAND。
所以这里就不展示了,但是在后面的章节我们会介绍u-boot才裁切,裁切之后u-boot只有两百多kb,这时候就能使用MiniTools下载到NAND FLASH中了。Mini440之uboot移植之裁剪、分区与环境变量设置(五)
3.4 下载到NOR FALSH
我们利用Jlink将代码下载到NOR FLASH中,并从NOR启动。串口输出信息如下:
initcall: 33f5dc2c U-Boot 2016.05 (Jan 14 2022 - 22:22:01 +0800) initcall: 33f0f3c0 U-Boot code: 33F00000 -> 33F80000 BSS: -> 33FCF870 initcall: 33f007a8 CPUID: 32440001 FCLK: 400 MHz HCLK: 100 MHz PCLK: 50 MHz initcall: 33f0f620 DRAM: initcall: 33f00518 initcall: 33f0f570 Monitor len: 000CF870 Ram size: 04000000 Ram top: 34000000 initcall: 33f0f1b0 initcall: 33f0f368 TLB table from 33ff0000 to 33ff4000 initcall: 33f0f1c8 initcall: 33f0f330 Reserving 830k for U-Boot at: 33f00000 initcall: 33f0f304 Reserving 4160k for malloc() at: 33af0000 initcall: 33f0f524 Reserving 80 Bytes for Board Info at: 33aeffb0 initcall: 33f0f1d0 initcall: 33f0f2d0 Reserving 168 Bytes for Global Data at: 33aeff08 initcall: 33f0f258 initcall: 33f0f204 initcall: 33f0f1d8 initcall: 33f0f610 initcall: 33f0f49c RAM Configuration: Bank #0: 30000000 64 MiB DRAM: 64 MiB initcall: 33f0f23c New Stack Pointer is: 33aefee0 initcall: 33f0f460 initcall: 33f0f3f0 Relocation Offset is: 00000000 Relocating to 33f00000, new gd at 33aeff08, sp at 33aefee0 initcall: 33f0f69c initcall: 33f0f6a4 initcall: 33f0f888 (relocated to 33f0f888) WARNING: Caches not enabled initcall: 33f0f6bc (relocated to 33f0f6bc) initcall: 33f0f6e4 (relocated to 33f0f6e4) initcall: 33f0f86c (relocated to 33f0f86c) using memory 0x33af0000-0x33f00000 for malloc() initcall: 33f0f6ec (relocated to 33f0f6ec) initcall: 33f0f678 (relocated to 33f0f678) initcall: 33f0f858 (relocated to 33f0f858) initcall: 33f184b4 (relocated to 33f184b4) initcall: 33f0f848 (relocated to 33f0f848) initcall: 33f0f7b0 (relocated to 33f0f7b0) Now running in RAM - U-Boot at: 33f00000 initcall: 33f0f6f4 (relocated to 33f0f6f4) initcall: 33f0f7cc (relocated to 33f0f7cc) Flash: fwc addr 00000000 cmd f0 00f0 16bit x 16 bit fwc addr 0000aaaa cmd aa 00aa 16bit x 16 bit fwc addr 00005554 cmd 55 0055 16bit x 16 bit fwc addr 0000aaaa cmd 90 0090 16bit x 16 bit fwc addr 00000000 cmd f0 00f0 16bit x 16 bit JEDEC PROBE: ID 1 2249 0 Found JEDEC Flash: AMD AM29LV160DB unlock address index 1 unlock addresses are 0x555/0x2aa erase_region_count = 1 erase_region_size = 16384 erase_region_count = 2 erase_region_size = 8192 erase_region_count = 1 erase_region_size = 32768 erase_region_count = 31 erase_region_size = 65536 flash_protect ON: from 0x00000000 to 0x0007F213 protect on 0 protect on 1 protect on 2 protect on 3 protect on 4 protect on 5 protect on 6 protect on 7 protect on 8 protect on 9 protect on 10 flash_protect ON: from 0x00070000 to 0x0007FFFF protect on 10 2 MiB initcall: 33f0f794 (relocated to 33f0f794) NAND: board_nand_init() end of nand_init hwcontrol(): 0xff 0x83 hwcontrol(): 0xffffffff 0x81 dev_ready hwcontrol(): 0x90 0x83 hwcontrol(): 0x00 0x85 hwcontrol(): 0xffffffff 0x81 dev_ready hwcontrol(): 0x90 0x83 hwcontrol(): 0x00 0x85 hwcontrol(): 0xffffffff 0x81 dev_ready hwcontrol(): 0x90 0x83 hwcontrol(): 0x40 0x85 hwcontrol(): 0xffffffff 0x81 dev_ready 256 MiB initcall: 33f0f764 (relocated to 33f0f764) *** Warning - bad CRC, using default environment Destroy Hash Table: 33f79a34 table = 00000000 Create Hash Table: N=79 INSERT: table 33f79a34, filled 1/79 rv 33af50d8 ==> name="bootdelay" value="5" INSERT: table 33f79a34, filled 2/79 rv 33af4f98 ==> name="baudrate" value="115200" INSERT: table 33f79a34, filled 3/79 rv 33af4f48 ==> name="ipaddr" value="192.168.0.188" INSERT: table 33f79a34, filled 4/79 rv 33af5100 ==> name="serverip" value="192.168.0.200" INSERT: table 33f79a34, filled 5/79 rv 33af513c ==> name="ethaddr" value="08:00:3e:26:0a:5b" INSERT: table 33f79a34, filled 6/79 rv 33af5394 ==> name="netmask" value="255.255.255.0" INSERT: free(data = 33af4e88) INSERT: done initcall: 33f0f68c (relocated to 33f0f68c) initcall: 33f18574 (relocated to 33f18574) initcall: 33f0f754 (relocated to 33f0f754) initcall: 33f161c8 (relocated to 33f161c8) In: serial Out: serial Err: serial Initial value for argc=3 Final value for argc=3 Initial value for argc=3 Final value for argc=3 Initial value for argc=3 Final value for argc=3 initcall: 33f00fc4 (relocated to 33f00fc4) initcall: 33f0f744 (relocated to 33f0f744) initcall: 33f0f724 (relocated to 33f0f724) initcall: 33f0f708 (relocated to 33f0f708) Net: Initial value for argc=3 Final value for argc=3 dm9000 initcall: 33f0f6fc (relocated to 33f0f6fc) ### main_loop entered: bootdelay=5 ### main_loop: bootcmd="<UNDEFINED>" SMDK2440 #
如果屏蔽掉调试信息:
四、代码下载
Young / s3c2440_project[u-boot-2016.05-nand-flash】
参考文章