1.将之前写过的bootloader中init.c拷贝到到单板目录smdk2440下
修改重名函数,避免相互调用出错
2.修改start.s
将原来的重定位代码重新进行修改
bl nand_init bl nand_init_ll
mov r0, #0 mov r0, #0 //原地址
ldr r1, =_start ldr r1,_TEXT_BASE //链接地址 不用ldr r1, =_start是因为_start的地址可能在4k之外
ldr r2, =__bss_start -----> ldr r2, _bss_start_ofs //需要拷贝代码的长度
sub r2, r2, r1
bl copy_code_to_sdram
bl copy_code_to_sdram bl clear_bss
bl clear_bss
#define CONFIG_SYS_TEXT_BASE 0x33f80000 //预留1M的空间给uboot
删除原来的重定位代码, board_init_f里也屏蔽掉relocate_code(addr_sp, id, addr);
ldr pc,=call_board_init_f //跳到链接地址去运行
/* Set stackpointer in internal RAM to call board_init_f */
call_board_init_f:
ldr r0,=0x00000000
bl board_init_f /* arch/arm/lib */
/*
...
...
*/
/*
* We are done. Do not return, instead branch to second part of board
* initialization, now running from RAM.
*/
#ifdef CONFIG_NAND_SPL
ldr r0, _nand_boot_ofs
mov pc, r0
3.修改启动第二阶段的代码
原来void board_init_r(gd_t *id, ulong dest_addr)函数的参数一个是gd_t结构体的地址,一个是程序的链接的地址,现在自己传进去,修改代码让board_init_f()函数返回id的地址
bl board_init_f /* arch/arm/lib */
/*board_init_f返回的是gd_t的地址,存在r0中*/
ldr r1,_TEXT_BASE
bl board_init_r //直接调用第二阶段的函数
4.替换代码,修改makefile 重新编译
首先修改 smdk2440/Makefile
LIB = $(obj)lib$(BOARD).o
COBJS := smdk2410.o init.o
SOBJS := lowlevel_init.o
SRCS := $(SOBJS:.o=.S) $(COBJS:.o=.c)
OBJS := $(addprefix $(obj),$(COBJS))
SOBJS := $(addprefix $(obj),$(SOBJS))
去除-pie
搜索找到定义 -pie的文件 grep "\-pie" * -nR
arch/x86/config.mk:43:LDFLAGS_FINAL += --gc-sections -pie
arch/arm/config.mk:75:LDFLAGS_u-boot += -pie
doc/README.arm-relocation:3:At arch level: add linker flag -pie
修改arm/config.mk文件,注释掉 -pie
修改链接脚本,把start.S,init.c, lowlevel.S 等文件放在最前面
修改 arch/arm/cpu/u-boot.lds
.text :
{
__image_copy_start = .;
CPUDIR/start.o (.text)
board/samsung/smdk2440/libsmdk2440.o(.text)
*(.text)
}
将修改的文件
arch/arm/lib/board.c
arm920t/start.s
smdk2440/int.c
configs/smdk2440.h
include/common.h
smdk2440/Makefile
arm/config.mk
arch/arm/cpu/u-boot.lds 进行替换
下载到nand上进行测试:
usb 1 30000000 打开dnw下载自己编译好的uboot.bin
nand erase 0 80000
nand write 30000000 0 80000
测试完,nand启动的结果:
U-Boot 2012.04.01 (Sep 21 2019 - 09:45:59)
CPUID: 32440001
FCLK: 400 MHz
HCLK: 100 MHz
PCLK: 50 MHz
DRAM: 64 MiB
WARNING: Caches not enabled
Flash: *** failed ***
### ERROR ### Please RESET the board ###
搜索错误代码 “Flash:”,找到启动失败的原因,定位到第二阶段启动函数中
puts("Flash: ");
flash_size = flash_init();
if (flash_size > 0) {
# ifdef CONFIG_SYS_FLASH_CHECKSUM
char *s = getenv("flashchecksum");
print_size(flash_size, "");
/*
* Compute and print flash CRC if flashchecksum is set to 'y'
*
* NOTE: Maybe we should add some WATCHDOG_RESET()? XXX
*/
if (s && (*s == 'y')) {
printf(" CRC: %08X", crc32(0,
(const unsigned char *) CONFIG_SYS_FLASH_BASE,
flash_size));
}
putc('\n');
# else /* !CONFIG_SYS_FLASH_CHECKSUM */
print_size(flash_size, "\n");
# endif /* CONFIG_SYS_FLASH_CHECKSUM */
} else {
puts(failed);
hang(); //打印错误信息,卡死代码
}
报错主要是因为没有识别出norflash,修改flash_init()函数来支持norflash的功能
首先打开调试命令,以支持debug调试命令,确定是哪个部分的导致flash初始化失败
#define DEBUG // drive/mtd/cfi_flash.c
#define _DEBUG 1
然后定位到 flash_detect_legacy(phys_addr_t base, int banknum) 函数问题
flash_read_jedec_ids(info);
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])) //匹配flash就是这里失败,然后直接返回 根据打印的 id和芯片手册的id进行比较
break;
最后推导是jedec_table[i] 数组中没有存在的flash类型,这里只有8位的flash,跟打印出来的信息不一致,需要进行修改
{
.mfr_id = (u16)MX_MANUFACT, //厂家id
.dev_id = MX29LV040, //设备ID
.name = "MXIC MX29LV040",
.uaddr = {
[0] = MTD_UADDR_0x0555_0x02AA /* x8 */ //解锁命令
},
.DevSize = SIZE_512KiB, //内存大小
.CmdSet = P_ID_AMD_STD, //指令集
.NumEraseRegions= 1, //擦除区域
.regions = {
ERASEINFO(0x10000, 8),
}
},
查看norflash的芯片手册,首先是厂家ID:00C2 和设备ID: 2249
norflash不能直接写,每次发送写命令之前需要先发送解锁命令,写命令
擦除结构是指flash里面扇区的结构,根据手册可以知道共有四种类型的扇区
{
.mfr_id = (u16)MX_MANUFACT, //这款板子上的flash c2 2249
.dev_id = 0x2249, //设备id
.name = "MXIC MX29LV160DB",
.uaddr = {
[1] = MTD_UADDR_0x0555_0x02AA /* 改成x16 的解锁地址(nor发出真正的写命令之前要先发解锁命令)*/
},
.DevSize = SIZE_2MiB, //总大小
.CmdSet = P_ID_AMD_STD, //命令集
.NumEraseRegions= 4, //擦除区域
.regions = {
ERASEINFO(16*1024, 1), //要擦除区域的大小和块的个数(如果擦除区域=2,这个数组就会有两个擦除元素)
ERASEINFO(8*1024, 2),
ERASEINFO(32*1024, 1),
ERASEINFO(64*1024, 31),
}
},
改完报错 “too many flash....”
smdk2440.h #define CONFIG_SYS_MAX_FLASH_SECT (128) //修改扇区数
修改完成,再将宏给屏蔽掉,重新下载运行:
U-Boot 2012.04.01 (Sep 23 2019 - 14:20:53)
CPUID: 32440001
FCLK: 400 MHz
HCLK: 100 MHz
PCLK: 50 MHz
DRAM: 64 MiB
WARNING: Caches not enabled
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 c2 2249 0
2 MiB
*** Warning - bad CRC, using default environment
In: serial
Out: serial
Err: serial
Net: CS8900-0
修改栈指针,之前没有设置完成 start.s board.c
ldr sp,base_sp
/*重新设置栈,否则栈指针还是指向0x30000f80,因为重定位函数不再是relocate_code(addr_sp, id, addr),所以board_init_f现在没有重新设置栈*/
addr_sp &= ~0x07; //8字节对齐后,sp指针确定
#else
addr_sp += 128; /* leave 32 words for abort-stack */
gd->irq_sp = addr_sp;
#endif
base_sp = addr_sp; //传回sp指针
debug("New Stack Pointer is: %08lx\n", addr_sp);
修改完支持norflash之后,更改代码支持nandflash
首先打开nandflash的开关
#define CONFIG_CMD_NAND
然后编译程序会报错是因为此时只有2410的nand初始化函数,没有2440的nand初始化的函数,根据错误信息来修改对应的代码,首先将原来2410的s3c2410_nand.c复制成时s3c2440_nand.c,在这个基础上修改,修改makefile支持编译s3c2440_nand.c, 首先加入2440的开关,以支持2440对nand的支持
/*
* NAND configuration
*/
#ifdef CONFIG_CMD_NAND
#ifdef CONFIG_S3C2410
#define CONFIG_NAND_S3C2410
#define CONFIG_SYS_S3C2410_NAND_HWECC
#else
#define CONFIG_NAND_S3C2440
#define CONFIG_SYS_S3C2440_NAND_HWECC
#endif
#define CONFIG_SYS_MAX_NAND_DEVICE 1
#define CONFIG_SYS_NAND_BASE 0x4E000000
#endif
然后修改nand的makefile以支持对2440
查看nand的初始化函数,来逐步查看需要重新设置的内容
void nand_init(void)
{
#ifdef CONFIG_SYS_NAND_SELF_INIT
board_nand_init();
#else
int i;
for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++)
nand_init_chip(i);
#endif
printf("%lu MiB\n", total_nand_size / 1024);
#ifdef CONFIG_SYS_NAND_SELECT_DEVICE
/*
* Select the chip in the board/cpu specific driver
*/
board_nand_select_device(nand_info[nand_curr_device].priv, nand_curr_device);
#endif
}
CONFIG_SYS_NAND_SELF_INIT宏未定义,所以对nand_init_chip(i)函数进行分析,需要对nand_scan(mtd, maxchips)和board_nand_init(nand)进行分析
#ifndef CONFIG_SYS_NAND_SELF_INIT
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);
}
#endif
首先是 board_nand_init()函数,原来的nand配置寄存器不是当前2440的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();
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
#ifdef CONFIG_S3C2410
cfg = S3C2410_NFCONF_EN;
cfg |= S3C2410_NFCONF_TACLS(tacls - 1);
cfg |= S3C2410_NFCONF_TWRPH0(twrph0 - 1);
cfg |= S3C2410_NFCONF_TWRPH1(twrph1 - 1);
#else
cfg |= ((tacls-1)<<12)|((twrph0-1)<<8)|((twrph1-1)<<4); //设置配置寄存器 调整时序
#endif
writel(cfg, &nand_reg->nfconf);
cfg = 0;
cfg |= (1<<4)|(1<<1)|(1<<0);
writel(cfg, &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 = 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 = s3c2410_nand_enable_hwecc;
nand->ecc.calculate = s3c2410_nand_calculate_ecc;
nand->ecc.correct = s3c2410_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_S3C2410_NAND_BBT
nand->options = NAND_USE_FLASH_BBT;
#else
nand->options = 0;
#endif
debug("end of nand_init\n");
return 0;
然后需要修改s3c2440_hwcontrol和s3c2440_dev_ready
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(); //4E000000
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 |= S3C2410_ADDR_NCLE;
if (!(ctrl & NAND_ALE))
IO_ADDR_W |= S3C2410_ADDR_NALE;
chip->IO_ADDR_W = (void *)IO_ADDR_W;
if (ctrl & NAND_NCE) //使能片选(与2410区别)
writel(readl(&nand->nfcont) & ~(1<<1),
&nand->nfcont);
else //禁止片选
writel(readl(&nand->nfcont) | (1<<1),
&nand->nfcont);
}
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();
debug("dev_ready\n");
return readl(&nand->nfstat) & 0x01;
}
紧接着对nand_scan函数进行修改
nand_scan
nand_scan_ident
nand_set_defaults //设置读写,选中等命令的指向
chip->select_chip = nand_select_chip;
chip->cmdfunc = nand_command;
chip->read_byte = busw ? nand_read_byte16 : nand_read_byte;
nand_get_flash_type
chip->select_chip
chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
*maf_id = chip->read_byte(mtd);
*dev_id = chip->read_byte(mtd);
从上面的逻辑顺序可以看出,nand_scan实现的功能就是选择芯片,发送地址数据命令等,最后调用的是nand_select_chip和nand_command等函数,这些函数调用的是chip->cmd_ctrl,然而在board_nand_init时,就已经指定了chip->cmd_ctrl的指向nand->cmd_ctrl = s3c2440_hwcontrol,从程序中可以看出原来的选择芯片和发送地址数据的命令需要修改
/*这个函数最后指向chip->select_chip
指向多个函数 实现
ctrl 选中芯片/取消选中 发命令/发地址
cmd 命令值 地址值
*/
static void s3c2440_hwcontrol(struct mtd_info *mtd, int data, unsigned int ctrl)
{
struct nand_chip *chip = mtd->priv;
struct s3c2440_nand *nand = s3c2440_get_base_nand(); //4E000000
if(ctrl & NAND_CLE){
writeb(data, &nand->nfcmd); //发命令
}else if(ctrl & NAND_ALE){
writeb(data, &nand->nfaddr); //发地址
}
}
nand->select_chip = s3c2440_nand_select; //需要自己添加选中芯片的函数
static void s3c2440_nand_select(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: //选中
nand->nfcont &= ~(1<<1)
break;
default:
BUG();
}
}
修改过的文件:
smdk2440.h
drive/mtd/nand/makefile
s3c2440_nand.c