移植u-boot-2012.07到mini2440开发板的笔记
<
1363976004@qq.com>
下载、解压、编译、u-boot源码
tar xjf u-boot-2012.07.tar.bz2
cd u-boot-2012.04.01
make smdk2410_config
Make
烧写到mini2440开发板的Nor flash中,启动开发板,观察现象。
创建mini2440单板
将u-boot-2012.07/board/samsung目录下的smdk2410复制为mini2440,将该目录下的smdk2410.c命名为mini2440.c,修改mini2440目录下的Makefile,将COBJS:= smdk2410.o改为COBJS:= mini2440.o
在board.cfg的65行的
smdk2410 arm arm920t - samsung s3c24x0
后面添加
mini2440 arm arm920t - samsung s3c24x0
在u-boot-2012.07/include/configs中的smdk2410.h复制为mini2440.h
阅读u-boot代码分析启动过程并修改代码
set the cpu to SVC32 mode
turn off the watchdog
设置时钟分频系数,使用默认的时钟120MHz
设置内存控制器
board_init_f(start.S)
-->for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) { (board.c)
if ((*init_fnc_ptr)() != 0) {
hang ();}
-->board_early_init_f(smdk2410.c) (该函数中设置系统时钟)
-->serial_init(serial_s3c24x0.c)
-->serial_init_dev(serial_s3c24x0.c)
-->_serial_setbrg(serial_s3c24x0.c)
-->get_PCLK(speed.c)
-->get_HCLK(speed.c)
........(发现宏CONFIG_S3C2440未定义,故使用的是2410的时钟,故应定义宏CONFIG_S3C2440)
【通过以上的分析可知:在设置时钟与设置内存控制器的顺序是先设置内存控制器,再设置系统时钟,故存在问题。故应先设置时钟,在已设置的时钟下,在设置内存控制器,让内存工作在合适的频率下,使其SDRAM正常工作】
更改:
在start.S中:
将
/* FCLK:HCLK:PCLK = 1:2:4 */
/* default FCLK is 120 MHz ! */
ldr r0, =CLKDIVN
mov r1, #3
str r1, [r0]
改为
#define S3C2440_MPLL_400MHZ ((0x5c<<12)|(0x01<<4)|(0x01))
/* 2. 设置时钟 */
ldr r0, =0x4c000014
mov r1, #0x05; // FCLK:HCLK:PCLK=1:4:8
str r1, [r0]
/* 如果HDIVN非0,CPU的总线模式应该从“fast bus mode”变为“asynchronous bus mode” */
mrc p15, 0, r1, c1, c0, 0 /* 读出控制寄存器 */
orr r1, r1, #0xc0000000 /* 设置为“asynchronous bus mode” */
mcr p15, 0, r1, c1, c0, 0 /* 写入控制寄存器 */
/* MPLLCON = S3C2440_MPLL_200MHZ */
ldr r0, =0x4c000004
ldr r1, =S3C2440_MPLL_400MHZ
str r1, [r0]
/* 启动ICACHE */
mrc p15, 0, r0, c1, c0, 0 @ read control reg
orr r0, r0, #(1<<12)
mcr p15, 0, r0, c1, c0, 0 @ write it back
在mini2440.c的board_early_init_f函数中去掉:
writel(0xFFFFFF, &clk_power->locktime);
writel((M_MDIV << 12) + (M_PDIV << 4) + M_SDIV,
&clk_power->mpllcon);
(在start.S中已经设置了时钟)
通过之前的分析可知:u-boot的源代码中已经带有了2440的串口初始化函数,只需在配置文件mini2440.h中定义宏CONFIG_S3C2440,去掉CONFIG_S3C2410即可。
将#define CONFIG_S3C2410改为 #define CONFIG_S3C2440
放在linux编译,发现错误,找出出现错误的文件,并找到该目录下的Makefile,找出控制该文件的宏,在mini2440.h文件中去掉该宏。然后在编译,如再有错误在重复以上步骤,直到编译成功。(只需去掉宏CONFIG_CMD_NAND的定义)
将生成的u-boot.bin烧如Nor flash中启动开发板,可以看到开发板可以看到有信信息输出,证明该u-boot的对2440的时钟以及串口的支持已经正常。
在串口上打印的信息中有 ### ERROR ### Please RESET the board ###
搜索 Please RESET the board可以直到该信息是在board.c中答应出来的
分析代码可知:
if (flash_size > 0) {
....................
}
else
{
puts(failed);
hang();
--> puts("### ERROR ### Please RESET the board ###\n");
for (;;);
}
可知该u-boot只支持nor flash启动,当不能识别nor flash时就打印错误,并让程序进入死循环,但是mini2440的开发板既支持nor flash启动也支持nand flash启动。当开发板从nand flash启动时,cpu看到的零地址是片内内存的地址,这是cpu根本就不能看到nor flash。
故应去掉board.c中:
puts(failed);
hang();
并加上:
puts("0 KB\n"); (当不能识别nor flash时打印出flash: 0KB,但程序还是继续运行。)
当从nand flash启动时,在上电后在硬件上,会将nand flash中前4k的代码复制到s3c2440的片内内存中,cpu此时看到的零地址是片内内存,即从片内内存开始执行,又由于u-boot的代码远大于4k,故在复制的4k的代码中应完成以下几件事:
初始化内存控制器,是的SDRAM能正常工作
将nand flash中的代码复制到SDRAM中
当以nor flash启动时,nor flash能像读内存一样的读,不能像写内存一样的写,cpu可以直接在nor flash上取得指令执行,故在开始就要做得事情就是要将nor flash上的代码复制到SDRAM中,让代码能正常运行。
通过分析u-boot的源代码可知,代码中的重定位代码是从内存的最高地址,减去需要使用的地址,算出代码重点位的起始地址。然后将代码修改后复制到SDRAM中,但是这种做法,很复杂,从定位代码大于4k,故不适用源代码中的方式,而采用将代码固定的复制到某个地址,在前4K的代码使用位置无关码。这样就使代码简单,在4k的代码中可以完成。
当以nand flash启动时,要将nand flash中的代码复制到SDRAM中,故应该提供nand flash的读函数,即在单板目录下添加mini2440_init.c文件并修改该目录下的Makefile,代码如下:
/* NAND FLASH控制器 */
#define NFCONF (*((volatile unsigned long *)0x4E000000))
#define NFCONT (*((volatile unsigned long *)0x4E000004))
#define NFCMMD (*((volatile unsigned char *)0x4E000008))
#define NFADDR (*((volatile unsigned char *)0x4E00000C))
#define NFDATA (*((volatile unsigned char *)0x4E000010))
#define NFSTAT (*((volatile unsigned char *)0x4E000020))
void nand_read(unsigned int addr, unsigned char *buf, unsigned int len);
static int isBootFromNorFlash(void)
{
volatile int *p = (volatile int *)0;
int val;
val = *p;
*p = 0x12345678;
if (*p == 0x12345678)
{
/* 写成功,是nand启动 */
*p = val;
return 0;
}
else
{
/* NOR不能像内存一样写 */
return 1;
}
}
void copy_code_to_sdram(unsigned char *src, unsigned char *dest, unsigned int len)
{
int i = 0;
/* 如果是nor启动 */
if (isBootFromNorFlash())
{
while (i < len)
{
dest
= src;
i++;
}
}
else
{
nand_read((unsigned int)src, dest, len);
}
}
void clear_bss(void)
{
extern int __bss_start, __bss_end__;
int *p = &__bss_start;
for (; p < &__bss_end__; p++)
*p = 0;
}
void my_nand_init(void)
{
#define TACLS 0
#define TWRPH0 1
#define TWRPH1 0
/* 设置时序*/
NFCONF = (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4);
/* 使能nand flash控制器,初始化ECC,禁止片选 */
NFCONT = (1<<4)|(1<<1)|(1<<0);
}
static void nand_select(void)
{
NFCONT &= ~(1<<1);
}
static void nand_deselect(void)
{
NFCONT |= (1<<1);
}
static void nand_cmd(unsigned char cmd)
{
volatile int i;
NFCMMD = cmd;
for (i = 0; i < 10; i++);
}
static void nand_addr(unsigned int addr)
{
unsigned int col = addr % 2048;
unsigned int page = addr / 2048;
volatile int i;
NFADDR = col & 0xff;
for (i = 0; i < 10; i++);
NFADDR = (col >> 8) & 0xff;
for (i = 0; i < 10; i++);
NFADDR = page & 0xff;
for (i = 0; i < 10; i++);
NFADDR = (page >> 8) & 0xff;
for (i = 0; i < 10; i++);
NFADDR = (page >> 16) & 0xff;
for (i = 0; i < 10; i++);
}
static void nand_wait_ready(void)
{
while (!(NFSTAT & 1));
}
static unsigned char nand_data(void)
{
return NFDATA;
}
void nand_read(unsigned int addr, unsigned char *buf, unsigned int len)
{
int col = addr % 2048;
int i = 0;
/* 1.选中 */
nand_select();
while (i < len)
{
/* 2. 发出读命令00h */
nand_cmd(0x00);
/* 3. 发出地址(分5步发出) */
nand_addr(addr);
/* 4. 发现读命令30h */
nand_cmd(0x30);
/* 5. 判断状态 */
nand_wait_ready();
/* 6. 读数据*/
for (; (col < 2048) && (i < len); col++)
{
buf = nand_data();
i++;
addr++;
}
col = 0;
}
/* 7. 取消选中*/
nand_deselect();
}
将board.c中的void board_init_f(ulong bootflag)函数改为返回id的具有返回值的函数
并在start.S中定义一个变量,在board.c函数中赋值,该变量用于存放栈指针。
因为在start.S中已经完成了重定位,故在board_init_f函数中去掉relocate_code
将start.S中的
call_board_init_f:
ldr sp, =(CONFIG_SYS_INIT_SP_ADDR)
bic sp, sp, #7 /* 8-byte alignment for ABI compliance */
ldr r0,=0x00000000
bl board_init_f
改为:
ldr sp, =(CONFIG_SYS_INIT_SP_ADDR)
bic sp, sp, #7 /* 8-byte alignment for ABI compliance */
bl my_nand_init /* 初始化nand 控制器 */
mov r0, #0 // 源
ldr r1, _TEXT_BASE // 目的
ldr r2, _bss_start_ofs // 长度
bl copy_code_to_sdram // 从定位代码
bl clear_bss // 清bss段
ldr pc,=call_board_init_f // 跳到SDRAM中执行
/* Set stackpointer in internal RAM to call board_init_f */
call_board_init_f:
ldr r0,=0x00000000
bl board_init_f
ldr r1, _TEXT_BASE // call_board_init_f 函数的返回值存放在r0中,刚好让 // board_init_r函数使用
// 函数使用
ldr sp, sp_addr_base // 重新设置栈
bl board_init_r // 进入第二阶段
在mini2440.h中将
#define CONFIG_SYS_TEXT_BASE 0x0
改为
#define CONFIG_SYS_TEXT_BASE 0x33f80000 // 重定位代码的基地址为33f80000 (在高地址处留出512k供u-boot运行)
原来的代码在链接时加了"-pie"选项, 使得u-boot.bin里多了"*(.rel*)", "*(.dynsym)"
故应去掉 "-pie"选项
arch/arm/config.mk中LDFLAGS_u-boot += -pie 去掉这行
修改链接脚本: 把start.S, init.c, lowlevel.S等文件放在最前面
即在arch/arm/cpu/u-boot.lds文件在CPUDIR/start.o后面添加:
board/samsung/smdk2440/libsmdk2440.o
移植u-boot支持nor flash的烧写与擦除
通过之前的移植,在串口上有输出,为了得到了nor flash的厂家id和设备id,所以在代码中添加了一些打印信息,而这些打印信息是通过宏开关决定的,分析代码可知,只需在cfi_flash.c文件中添加
#define DEBUG 1
#define _DEBUG 1
就可以打开宏开关,得到打印信息,然后编译烧写,观察串口的打印信息。信息如下:
Flash: fwc addr (null) 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 (null) cmd f0 00f0 16bit x 16 bit
JEDEC PROBE: ID 1 2249 0
fwc addr (null) cmd ff 00ff 16bit x 16 bit
fwc addr (null) cmd 90 0090 16bit x 16 bit
fwc addr (null) cmd ff 00ff 16bit x 16 bit
JEDEC PROBE: ID 14 ea00 0
*** Warning - bad CRC, using default environment
可以得到nor flash的厂家id和设备id分别为1、 0x2249
在代码中搜索JEDEC PROBE可以知道,该信息在代码中cfi_flash.c文件中打印出来的,代码为:
debug("JEDEC PROBE: ID %x %x %x\n",
info->manufacturer_id,
info->device_id,
info->device_id2);
if (jedec_flash_match(info, info->start[0]))
break;
-->jedec_flash_match
-->for (i = 0; i < ARRAY_SIZE(jedec_table); i++) {
if ((jedec_table.mfr_id & mask) == (info->manufacturer_id & mask) &&
(jedec_table.dev_id & mask) == (info->device_id & mask)) {
fill_info(info, &jedec_table, base);
ret = 1;
break;
}
可知:识别出ID后,在与数组jedec_table比较的时候没有匹配的,故应在jedec_table的定义处添加一项符合该nor flash的信息,即添加
{
.mfr_id = (u16)AMD_MANUFACT,
.dev_id = 0x2249,
.name = "ST Micro M29F400BB",
.uaddr = {
[1] = MTD_UADDR_0x0555_0x02AA /* x16 */
},
.DevSize = SIZE_2MiB,
.CmdSet = CFI_CMDSET_AMD_LEGACY,
.NumEraseRegions = 4,
.regions = {
ERASEINFO(0x04000, 1),
ERASEINFO(0x02000, 2),
ERASEINFO(0x08000, 1),
ERASEINFO(0x10000, 31),
}
},
在编译烧写,发现会打印出 ERROR: too many flash sectors
搜索代码可知是由于:
if (sect_cnt >= CONFIG_SYS_MAX_FLASH_SECT) {
printf("ERROR: too many flash sectors\n");
break;
故需要修改宏CONFIG_SYS_MAX_FLASH_SECT的值,将配置文件中mini2440.h中的
#define CONFIG_SYS_MAX_FLASH_SECT (19)
改为
#define CONFIG_SYS_MAX_FLASH_SECT (50)
(只需这个值打于35即可,因为该nor flash一共35个扇区)
为了使u-boot的命令行输入命令时,可以通过TAB键补全,只需在配置文件 mini2440.h中定义宏
#define CONFIG_AUTO_COMPLETE
修改输入命令行前面的提示SMDK2410 # :只需将配置文件 mini2440.h中定义宏
#define CONFIG_SYS_PROMPT "SMDK2410 # "
改为
#define CONFIG_SYS_PROMPT "[mini2440@u-boot]# "
这样在命令行前面的提示就变成了[mini2440@u-boot]#
修改u-boot,使其支持nand flash的烧写与读写工作。
在drivers/mtd/nand 目录下将s3c2410_nand.c复制为s3c2440_nand.c,并修改Makefile,通过宏CONFIG_NAND_S3C2440控制s3c2440_nand.c文件是否编译,在mini2440.h中添加
#define CONFIG_NAND_S3C2440
去掉
#define CONFIG_NAND_S3C2410
分析board.c,在board_init_r函数中发现:
#if defined(CONFIG_CMD_NAND)
puts("NAND: ");
nand_init(); /* go init the NAND */
#endif
所以在nand_init函数中初始化nand flash。
nand_init (board.c)
-->board_nand_init (nand.c)
-->nand_init_chip (nand.c)
-->nand_scan (nand_base.c)
-->nand_scan_ident (nand_base.c)
-->nand_set_defaults (nand_base.c)
-->nand_get_flash_type (nand_base.c)
-->nand_scan_tail (nand_base.c)
阅读s3c2440的芯片手册可知,应修改nand 控制器的时序:
将
#define S3C2440_NFCONF_TACLS(x) ((x)<<8)
#define S3C2440_NFCONF_TWRPH0(x) ((x)<<4)
#define S3C2440_NFCONF_TWRPH1(x) ((x)<<0)
改为
#define S3C2440_NFCONF_TACLS(x) ((x)<<12)
#define S3C2440_NFCONF_TWRPH0(x) ((x)<<8)
#define S3C2440_NFCONF_TWRPH1(x) ((x)<<4)
去掉:
cfg = S3C2440_NFCONF_EN;
为了搞nand flash的烧写速度,可以将
tacls = 4;
twrph0 = 8;
twrph1 = 8;
改为:
tacls = 4;
twrph0 = 8;
twrph1 = 8;
为了使能NAND Flash控制器, 初始化ECC, 禁止片选
应添加: cfg = (1<<4)|(1<<1)|(1<<0);
writel(cfg, &nand_reg->nfcont);
将board_nand_init函数中的
nand->select_chip = NULL;
改为
nand->select_chip = nand_select_chip;
并添加函数:
static void nand_select_chip(struct mtd_info *mtd, int chipnr)
{
struct s3c2440_nand *nand = s3c2440_get_base_nand();
switch (chipnr) {
case -1:
/* 取消选中 */
nand->nfcont |= (1<<1);
break;
case 0:
//chip->cmd_ctrl(mtd, 0, 0 | NAND_CTRL_CHANGE);
/* 选中 */
nand->nfcont &= ~(1<<1);
break;
default:
BUG();
}
将函数S3C2440_hwcontrol改为:
static void S3C2440_hwcontrol(struct mtd_info *mtd, int dat, unsigned int ctrl)
{
struct s3c2440_nand *nand = s3c2440_get_base_nand();
if (ctrl & NAND_CLE)
{
/* 写命令 */
writeb(dat, &nand->nfcmd);
}
else if(ctrl & NAND_ALE)
{
/* 写地址 */
writeb(dat, &nand->nfaddr);
}
}
在配置文件中定义宏:
#define CONFIG_s3c2440_nand_HWECC
修改u-boot支持DM9000网卡
阅读代码发现,在u-boot的源代码中已经支持了DM9000的代码,只需添加进去即可。
查看 drivers/net/Makefile,发现dm9000x的编译由宏CONFIG_DRIVER_DM9000控制,所以必须在配置文件中定义宏。
修改配置文件mini2440.h
将
#define CONFIG_CS8900
#define CONFIG_CS8900_BASE 0x19000300
#define CONFIG_CS8900_BUS16 */
改为:
#define CONFIG_DRIVER_DM9000
#define CONFIG_DM9000_BASE 0x20000000
#define DM9000_IO CONFIG_DM9000_BASE
#define DM9000_DATA (CONFIG_DM9000_BASE + 4)
在mini2440.c中的函数board_eth_init中添加
dm9000_initialize(dis);
添加默认参数:
在mini2440.h的配置文件中添加默认参数:
#define CONFIG_NETMASK 255.255.255.0
#define CONFIG_IPADDR 192.168.1.128
#define CONFIG_SERVERIP 192.168.1.125
#define CONFIG_ETHADDR 00:0c:29:4d:e4:f4
#define CONFIG_BOOTARGS "console=ttySAC0,115200 root=/dev/mtdblock3"
#define CONFIG_BOOTCOMMAND "nand read 30000000 kernel;bootm 30000000"
设置环境变量的保存地址
在u-boot中保存环境变量的命令时saveenv,在代码中搜索saveenv知道,要想使环境变量保存在nand flash中,则必须将env_nand.c编译内u-boot中,查看common/Makefile 知道由宏CONFIG_ENV_IS_IN_NAND控制env_nand.c的编译,故应在配置文件mini2440.h中
#define CONFIG_ENV_IS_IN_NAND
而u-boot源码提供环境变量的保存位置是在nor flash中,故应去掉宏
#define CONFIG_ENV_ADDR (CONFIG_SYS_FLASH_BASE + 0x070000)
#define CONFIG_ENV_IS_IN_FLASH
#define CONFIG_ENV_SIZE 0x10000
#define CONFIG_ENV_OVERWRITE
然后编译,发现错误,提示需要定义宏CONFIG_ENV_OFFSET、CONFIG_ENV_SIZE
继续在mini2440.h的文件中定义
#define CONFIG_ENV_OFFSET 0x40000
#define CONFIG_ENV_SIZE 0x20000
添加分区
在配置文件中添加宏:
#define CONFIG_CMD_MTDPARTS
#define CONFIG_MTD_DEVICE
#define MTDIDS_DEFAULT "nand0=mini2440-0" /* 那一个设备 */
#define MTDPARTS_DEFAULT "mtdparts=mini2440-0:256k(u-boot)," \
"128k(params)," \
"5m(kernel)," \
"-(rootfs)"
在board.c的board_init_r函数中添加代码:
run_command("mtdparts default", 0);
为了能启动mini2440的linux内核,必须修改mini2440.c文件中
将 board_init函数中的
gd->bd->bi_arch_number = MACH_TYPE_SMDK2410;
改为:
gd->bd->bi_arch_number = MACH_TYPE_MINI2440;
修改u-boot支持yaffs文件系统的烧写
在配置文件中定义宏:
#define CONFIG_CMD_NAND_YAFFS
在common/cmd_nand.c的第670行左右:
将
ret = nand_write_skip_bad(nand, off, &rwsize,
(u_char *)addr,
WITH_INLINE_OOB);
改为:
ret = nand_write_skip_bad(nand, off, &rwsize,
(u_char *)addr,
WITH_YAFFS_OOB);
在drivers/mtd/nand/nand_util.c中的第556行左右: 将
ops.mode = MTD_OOB_AUTO;
改为:
ops.mode = MTD_OOB_RAW;
注:该比较是我一遍移植,一边写的,应该不会有什么大问题,自己认为写的还较详细,希望对有需要的朋友有所帮助,有什么错误或者问题可以联系<1363976004@qq.com>
并指出,本人将不胜感激。