在之前的章节我们已经介绍了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重定位(三):
![](https://i-blog.csdnimg.cn/blog_migrate/09799a1e8544bdd09d2f669327e48e44.png)
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文件中的重定位规则检验的代码,否则编译汇报如下错误:
![](https://i-blog.csdnimg.cn/blog_migrate/08773c248772a7d9837226744630f8c7.png)
我们定位到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,并正常输出其大小。
![](https://i-blog.csdnimg.cn/blog_migrate/dd577bdeb7af1d9b5b2c861bc9d74230.png)
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如下:
![](https://i-blog.csdnimg.cn/blog_migrate/b58f86c85e27cf2fc19e2808b69f325b.png)
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函数进行修改。
完整代码:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
/*
* (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:
![](https://i-blog.csdnimg.cn/blog_migrate/6c6b8c107d35062e0903e38a9c7adadb.png)
继续查看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 #
如果屏蔽掉调试信息:
![](https://i-blog.csdnimg.cn/blog_migrate/be36b696e8cc7761b2d7c0b84f3b6c82.png)
四、代码下载
Young / s3c2440_project[u-boot-2016.05-nand-flash】
参考文章