04.移植u-boot
Posted wade_linux
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了04.移植u-boot相关的知识,希望对你有一定的参考价值。
1.读readme获取信息
1.1 由Building the Software可知,需修改顶层makefile,指定架构和编译器
ifeq ($(HOSTARCH),$(ARCH))
CROSS_COMPILE ?= arm-linux-
endif
ARCH = arm
CROSS_COMPILE = arm-linux-
2.新建一个单板
cd board/samsung/
cp smdk2410 smdk2440 -rf
cd ../../include/configs/
cp smdk2410.h smdk2440.h
3.仿造smdk2410修改文件
具体做法为
grep "smdk2410" * -nR建立并修改相应的文件
4.实验并烧写,查看结果
make smdk2440_config
make
5.下载到开发板后没有任何输出,阅读代码发现不足
在u-boot-2016.11\arch\arm\cpu\arm920t\start.S出了问题没有对MPLL
进行初始化/设置时钟,而是在board_init_f中board_early_init_f初始化
的,但是之前是按照64MHz设置的,而现在的2440只工作在12MHz,这样是
无法正常工作,所以应该把board_early_init_f里对MPLL的设置给注释掉,
在start.S关闭看门狗后进行时钟的设置。
5.2处理措施:
#if 0
/* FCLK:HCLK:PCLK = 1:2:4 */
/* default FCLK is 120 MHz ! */
ldr r0, =CLKDIVN
mov r1, #3
str r1, [r0]
#else
/* 2. 设置时钟 400MHz */
ldr r0, =0x4c000014
// mov r1, #0x03; // FCLK:HCLK:PCLK=1:2:4, HDIVN=1,PDIVN=1
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 /* 写入控制寄存器 */
#define S3C2440_MPLL_400MHZ ((0x5c<<12)|(0x01<<4)|(0x01))
/* MPLLCON = S3C2440_MPLL_400MHZ */
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
#endif
6.
①依照上述下载到2440开发板,串口输出乱码,这表示2440工作了,这时候出现
乱码应该是串口方面的问题,在board_init_f函数中调用了init_sequence_f数组
里面有一个串口初始化serial_init,点进去,里面有个get_current函数,再进
里面default_serial_console函数,再进,在这个函数所在的c文件(serial_s3c24x0.c)
中有个_serial_setbrg函数中里面有个get_PCLK函数进去该函数中有这么一句
”#ifdef CONFIG_S3C2440”但是这个宏没有被定义,这个代表定义它就支持了2440,我们就
在include/configs/smdk2440.h中定义一下就可以了.然后再重新编译,编译出来的u-boot_04.bin
下载到2440中我们就可以看到串口打印出信息了
②改完后编译,烧写,串口有输出了,但是乱码,估计是波特率的问题。分析代码
board_init_r
init_sequence_r
initr_serial
serial_initialize
s3c24xx_serial_initialize
serial_register(&s3c24xx_serial0_device)
s3c24xx_serial0_device
INIT_S3C_SERIAL_STRUCTURE
s3serial##port##_setbrg
serial_setbrg_dev
_serial_setbrg
get_PCLK
get_HCLK(#ifdef CONFIG_S3C2440)
7.支持NOR FLASH
board_init_r
init_sequence_r
initr_flash
flash_init
if (!flash_detect_legacy(cfi_flash_bank_addr(i), i))
flash_get_size(cfi_flash_bank_addr(i), i);
//用老方法检测是否识别出来,否则就用新方法
为什么老方法无法识别出nor flash?
flash_detect_legacy
jedec_flash_match
jedec_table//该数组里没有我们需要的nor flash的型号和参数,需要手动输入
{
.mfr_id = (u16)MX_MANUFACT,
.dev_id = 0x2249,
.name = "MX29LV160D",
.uaddr = {
[1] = MTD_UADDR_0x0555_0x02AA /* x16 */
},
.DevSize = SIZE_2MiB,
.CmdSet = P_ID_AMD_STD,
.NumEraseRegions= 4,
.regions = {
ERASEINFO(0x10000, 31),
ERASEINFO(0x08000, 1),
ERASEINFO(0x02000, 2),
ERASEINFO(0x04000, 1),
}
},
测试下norflash能否正确读写,用以下u-boot命令:
cp.b 0 30000000 80
cmp.b 0 30000000 80
发现读norflash没有问题。再用以下几条命令测试写norflash:
mw.b 30000000 12 3
protect off all
erase 0 ffff
cp.b 30000000 0 3
md.b 0 3;
发现也是121212;因此写norflash成功,至此u-boot已经支持JZ2440开发板的nor flash;
8.支持nand flash
支持了norflash后,发现nandflash未识别出来,启动uboot显示NAND: 0 Byte。在代码
中搜索"NAND:",定位到common目录中的board_r.c的initr_nand函数:
nand_init
nand_init_chip
board_nand_init
设置nand_chip结构体, 提供底层的操作函数
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);
nand_command() // 即可以用来发命令,也可以用来发列地址(页内地址)、行地址(哪一页)
chip->cmd_ctrl
s3c2440_hwcontrol
chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
*maf_id = chip->read_byte(mtd);
*dev_id = chip->read_byte(mtd);
board_nand_init函数主要与单板有关的初始化,uboot的开发人员已经把与单板无关的一些
协议设置完成了,而把与单板有关的初始化单独列为一层,我们需要做的仅仅是把单板相关的
这一层移植即可。因此这里主要关注board_nand_init函数。我们的board_nand_init函数在
drivers/mtd/nand/s3c2410_nand.c中定义了。进入其中仔细分析代码,发现对nfconf寄存器
的初始化,2410的设置并不适用于2440,于是修改代码如下:
//cfg = S3C2410_NFCONF_EN;
cfg |= S3C2410_NFCONF_TACLS(tacls - 1);
cfg |= S3C2410_NFCONF_TWRPH0(twrph0 - 1);
cfg |= S3C2410_NFCONF_TWRPH1(twrph1 - 1);
writel(cfg, &nand_reg->nfconf);
#define S3C2410_NFCONF_TACLS(x) ((x)<<12)
#define S3C2410_NFCONF_TWRPH0(x) ((x)<<8)
#define S3C2410_NFCONF_TWRPH1(x) ((x)<<4)
往下看,后面设置了2410的nandflash的底层函数,其中selcet_chip被设置为NULL了,我们在
nand_scan里往下看,nand_scan_ident到nand_set_defaults,如果某个底层函数是空,则会
为其分配一个默认的函数,其中的默认的select_chip函数并不完整,不能用,并且后面的代码
调用到select_chip函数,因此这里需要我们自己写一个select_chip函数。
在s3c2410_nand.c中定义如下:
static void s3c24x0_nandselect(struct mtd_info *mtd, int chipnr)
{
struct s3c24x0_nand *nand_reg = s3c24x0_get_base_nand();
switch (chipnr) {
case -1:
writel(readl(&nand_reg->nfcont) | (1 << 1),&nand_reg->nfcont);
break;
case 0:
writel(readl(&nand_reg->nfcont) & ~(1 << 1),&nand_reg->nfcont);
break;
default:
BUG();
}
}
在board_nand_init函数中设置select_chip函数
nand->select_chip = s3c24x0_nandselect;
继续往下分析,发现s3c24x0_hwcontrol函数不适用于2440,修改如下:
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 |= S3C2410_ADDR_NCLE;//NFADDR
if (!(ctrl & NAND_ALE))
IO_ADDR_W |= S3C2410_ADDR_NALE;//NFCMD
chip->IO_ADDR_W = (void *)IO_ADDR_W;
if (ctrl & NAND_NCE)
writel(readl(&nand->nfcont) & ~(1<<1),
&nand->nfcont);//chip select
else
writel(readl(&nand->nfcont) | (1<<1),
&nand->nfcont);//deselcet
}
if (cmd != NAND_CMD_NONE)
writeb(cmd, chip->IO_ADDR_W);
else //若cmd = NAND_CMD_NONE,写地址要恢复为nfdata
chip->IO_ADDR_W = (void *)&nand->nfdata;
}
这个s3c24x0_hwcontrol函数可以根据crtl值的不同更改写指针chip->IO_ADDR_W,
从而实现既能写地址,又能写命令,当crtl & NAND_CRTL_CHANGE=1时,chip->ADDR_W
就会被赋一次值。在nand_set_defaults中,chip->cmdfunc = nand_command,这个
函数里会多次调用chip->cmd_crtl(即我们的s3c24x0_hwcontrol函数)来实现发命令
和发地址。由于s3c2410_nand.c中未定义chip->cmdfunc,就会使用默认的nand_command。
这里最好将nand_command改为 nand_command_lp,因为我们的nandflash是大页的,每页数据
有2K+64个字节。仔细阅读nand_command_lp你就会了解s3c24x0_hwcontrol应该实现的功能。
在nand_command_lp中,我们发现每个阶段调用chip->cmd_crtl之后,最后都会调用
chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE)(写地址结束后调用一次,写命令结束后调用一次),
仔细思考你就会想到,chip->IO_ADDR_W在board_nand_init中是被初始化为指向nfdata的,
如果我们调用完s3c24x0_hwcontrol,chip->IO_ADDR_W是指向nfaddr或nfcmd,是否要将其还原呢
,否则下次如果要想写数据不是会出错吗(写数据不用调用chip->cmd_ctrl),
因此chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE)的用意应该是还原
chip->IO_ADDR_W为nfdata,所以上面的s3c24x0_hwcontrol的最后我加上了个else分支。经过实验,
不加这一句,nandflash读写会出错。
另外,S3C2410_ADDR_NCLE和S3C2410_ADDR_NALE两个宏要修改为如下:
#define S3C2410_ADDR_NALE 8
#define S3C2410_ADDR_NCLE 0xC
这里使用到了NFCONT寄存器,而2410并无该寄存器,之前未对其进行初始化,因此在
board_nand_init中要对该寄存器进行初始化设置:
/* 初始化ECC,禁止片选,使能NAND Flash控制器 */
writel((1<<4)|(1<<1)|(1<<0),&nand_reg->nfcont);
同时,s3c24x0_nand结构体中由于原来定义了CONFIG_S3C2410宏,导致没有NFCONT等寄存器,
因此在smdk2410.h中去掉该宏。编译,烧写到开发板,识别出nandflash为256M。我们再试试
Nandflash的读写。首先试试写nandflash,用以下命令:
nand erase 0 4
mw.l 30000000 12345678
nand write 30000000 0 4
nand dump 0
发现nandflash写入12345678成功。然后再试读nandflash,使用命令:
mw.l 30000000 22222222
md.l 30000000
nand read 30000000 0 4
md.l 30000000
发现成功读出12345678。至此u-boot已经成功支持JZ2440的nandflash。
9.支持DM9000
启动uboot,打印出Net: CS8900-0,而我们的网卡是DM9000,于是在代码中搜索“Net:”,
定位到common/board_r.c的initr_net函数,一路追踪eth_initialize,eth_common_init,
一直到board\samsung\smdk2410\smdk2410.c的board_eth_init函数:
int board_eth_init(bd_t *bis)
{
int rc = 0;
#ifdef CONFIG_CS8900
rc = cs8900_initialize(0, CONFIG_CS8900_BASE);
#endif
return rc;
}
这里是对CS8900进行了初始化,我们要对DM9000进行初始化,通过查看
drivers/net/Makefile,发现要包含dm9000x.c的文件,要定义CONFIG_DRIVER_DM9000
这个宏,我们也要注释掉CONFIG_CS8900宏。同时查看dm9000x.c,里面有一个
dm9000_initialize函数,于是仿照cs8900来写dm9000的初始化函数。
int board_eth_init(bd_t *bis)
{
int rc = 0;
#ifdef CONFIG_CS8900
rc = cs8900_initialize(0, CONFIG_CS8900_BASE);
#endif
#ifdef CONFIG_DRIVER_DM9000
rc = dm9000_initialize(bis);
#endif
return rc;
}
配置文件smdk2410.h修改如下:
//#define CONFIG_CS8900 /* we have a CS8900 on-board */
//#define CONFIG_CS8900_BASE 0x19000300
//#define CONFIG_CS8900_BUS16 /* the Linux driver does accesses as shorts */
#define CONFIG_DRIVER_DM9000
保存,编译,提示一大堆错误,提示dm9000x.c中的‘DM9000_DATA‘ undeclared, ‘DM9000_IO‘ undeclared等等,
结合上面的CS8900的一些宏,看来这些宏需要我们自己在smdk2410.h中定义,在linux中使用grep "DM9000_DATA" * -nR
命令搜索,看看其他板子的配置文件是如何定义这些宏的,可以适当参考下,结合原理图和2440手册,smdk.h中定义如下:
#define CONFIG_DRIVER_DM9000
#define CONFIG_DM9000_BASE 0x20000000
#define DM9000_IO CONFIG_DM9000_BASE
#define DM9000_DATA (CONFIG_DM9000_BASE + 4)
#define CONFIG_DM9000_USE_16BIT 1
第四行之所以加4是因为DM9000上的CMD引脚接到了LADDR2上。
编译,下载到开发板,提示错误Error: dm9000 address not set.
在代码中搜索“address not set”,有两个函数会打印这句话,分别是net/eth.c里的
eth_post_probe函数和eth_write_hwaddr函数。这个eth_post_probe函数是uboot里网卡
驱动的probe函数,而eth_write_hwaddr会在eth_initialize里被调用,因此这里应该是
eth_write_hwaddr打印出来的。阅读这个函数的代码,发现应该是未设置ethaddr即网卡
的MAC地址导致报错。进入到eth_getenv_enetaddr_by_index(base_name, eth_number, env_enetaddr)里分析:
int eth_getenv_enetaddr_by_index(const char *base_name, int index,
uchar *enetaddr)
{
char enetvar[32];
sprintf(enetvar, index ? "%s%daddr" : "%saddr", base_name, index);
return eth_getenv_enetaddr(enetvar, enetaddr);
}
分析得知,这个函数最终将存放MAC地址的环境变量的值存入env_enetaddr数组中,那么这个
环境变量名称由
sprintf(enetvar, index ? "%s%daddr" : "%saddr", base_name, index)确定。在eth_initialize里eth_write_hwaddr是这样被调用的:
eth_write_hwaddr(dev, "eth", dev->index)。上面的basename应该对应于"eth"。结合上面的sprintf函数,如果index函数存在,则
环境变量名称为eth<index>addr,否则为ethaddr。这里index的值即为eth_write_hwaddr(dev, "eth", dev->index)传入的dev->index,
在eth.c中搜索dev->index,看在哪里设置它的值。结果发现eth_register里会设置:
int eth_register(struct eth_device *dev)
{
struct eth_device *d;
static int index;
assert(strlen(dev->name) < sizeof(dev->name));
if (!eth_devices) {
eth_devices = dev;
eth_current = dev;
eth_current_changed();
} else {
for (d = eth_devices; d->next != eth_devices; d = d->next)
;
d->next = dev;
}
dev->state = ETH_STATE_INIT;
dev->next = eth_devices;
dev->index = index++;
return 0;
}
那么这个函数什么时候会被调用呢,搜索发现每一款网卡的文件里会调用这个函数来注册网卡,我们的DM9000x.c里也有,我们只有一个网卡,
因此只会调用一次,index是静态变量,未赋初始值则默认初始值为0,因此dev->index = index++(注意计算顺序),dec->index = 0,所以
环境变量名称为ethaddr。所以我们需要定义一个名为ethaddr环境变量。在include/env_default.h中有默认环境变量的设置,我们在其中
加入ethaddr环境变量的默认值,这个值我是参照电脑网卡MAC地址随便写的。
#elif defined(DEFAULT_ENV_INSTANCE_STATIC)
static char default_environment[] = {
#else
const uchar default_environment[] = {
#endif
#ifdef CONFIG_ETHADDR
"ethaddr=" CONFIG_ETHADDR "\0"
#endif
#ifdef CONFIG_ENV_CALLBACK_LIST_DEFAULT
ENV_CALLBACK_VAR "=" CONFIG_ENV_CALLBACK_LIST_DEFAULT "\0"
#endif
在smdk2410.h中设置CONFIG_ETHADDR宏:
#define CONFIG_ETHADDR "00:0c:29:91:dc:5f"
保存,编译,烧写,启动Uboot,网卡正常启动。设置一下开发板ip:set ipaddr 192.168.0.2。网线连接开发板和路由器,ping一下主机ip:
ping 192.168.0.100,能够Ping通。再试试能不能用tftp下载文件,
set serverip 192.168.0.100
tftp 30000000 u-boot.bin
使用tftp下载文件也成功,至此,DM9000网卡已经支持。
10.设置nand分区
在下载内核或文件系统时,我们可以直接在命令中写明烧到nandflash
的具体地址,但较麻烦,我们可以给nandflash分区,这样就可直接写
烧到那个分区就行了,较为方便。如何设置呢?首先我们在uboot中输入
mtdparts命令,看看默认的分区,结果提示mtdids not defined, no default present。
搜索"mtdids not defined",定位到common/cmd_mtdparts.c的mtdparts_init函数中,
分析发现是mtdids_default为空。mtdids以及另一个重要的变量mtdparts定义如下:
#if defined(MTDIDS_DEFAULT)
static const char *const mtdids_default = MTDIDS_DEFAULT;
#else
$ static const char *const mtdids_default = NULL;
#endif
#if defined(MTDPARTS_DEFAULT)
static const char *const mtdparts_default = MTDPARTS_DEFAULT;
#else
static const char *const mtdparts_default = NULL;
#endif
因此,我们需要在smdk2410.h中定义MTDIDS_DEFAULT,MTDPARTS_DEFAULT这两个宏。如何定义这两个宏呢,cmd_mtdparts.c中注释里有例子示范了:
/* Examples:
*
* 1 NOR Flash, with 1 single writable partition:
* mtdids=nor0=edb7312-nor
* mtdparts=mtdparts=edb7312-nor:-
*
* 1 NOR Flash with 2 partitions, 1 NAND with one
* mtdids=nor0=edb7312-nor,nand0=edb7312-nand
* mtdparts=mtdparts=edb7312-nor:256k(ARMboot)ro,-(root);edb7312-nand:-(home)
*
*/
结合例子和其他开发板的配置文件,我们的定义如下:
#define MTDIDS_DEFAULT "nand0=s3c2440-nand.0"
#define MTDPARTS_DEFAULT "mtdparts=s3c2440-nand.0:256k(bootloader),"\
"128k(params),2m(kernel),"\
"-(rootfs)"
保存,编译,烧写。启动u-boot后执行mtdparts命令,提示mtdparts variable not set, see ‘help mtdparts‘,no partitions defined
那就执行help mtdparts命令看看,发现这么一句:mtdparts default - reset partition table to defaults
可能要执行一下mtdparts default,执行后发现不再提示错误。但总不能每次都要手动执行一次命令吧。于是,我们在代码里执行这么一个命令。
在board_r.c的run_main_loop里修改如下:
static int run_main_loop(void)
{
#ifdef CONFIG_SANDBOX
sandbox_main_loop_init();
#endif
/* main_loop() can return to retry autoboot, if so just run it again */
run_command("mtdparts default",0);//添加这一行代码
for (;;)
main_loop();
return 0;
编译烧写后,启动u-boot执行mtdparts命令,不再提示错误,直接列出了分区,我们试着往kernel分区里烧写uImage,同时要想启动内核,
必须要设置默认参数bootargs和bootcmd,根据environment.h文件,我们要在smdk2410.h里设置CONFIG_BOOTARGS和CONFIG_BOOTCOMMAND两个宏,如下:
#define CONFIG_BOOTARGS "noinitrd root=/dev/mtdblock3 init=/linuxrc console=ttySAC0"
#define CONFIG_BOOTCOMMAND "nand read.jffs2 0x30007FC0 kernel; bootm 0x30007FC0"
编译烧写,启动u-boot,使用tftp下载uImage到30000000,使用下面的命令
nand erase.chip
nand write.jffs2 30000000 kernel 1c08e8
下载内核到kernel分区成功,并且已经可以启动内核了。
之前我们设置环境变量,都未执行save命令,因为我们还未设置环境变量保存地址,现在我们想让环境变量保存在我们设置的params分区上。
搜索saveenv函数,发现env_flash.c和env_nand.c都有这个函数,通过查看common/Makefile发现要通过定义CONFIG_ENV_IS_IN_NAND才能包含env_nand.c,
从而将环境变量保存在nandflash上。同时还要设置CMD_SAVEENV,CONFIG_ENV_RANGE,CONFIG_ENV_OFFSET等宏,修改配置文件如下:
#define CONFIG_ETHADDR "00:0c:29:91:dc:5f"
//#define CONFIG_ENV_ADDR (CONFIG_SYS_FLASH_BASE + 0x070000)
//#define CONFIG_ENV_IS_IN_FLASH
//#define CONFIG_ENV_SIZE 0x10000
#define CMD_SAVEENV
#define CONFIG_ENV_IS_IN_NAND
#define CONFIG_ENV_SIZE 0x20000
#define CONFIG_ENV_OFFSET 0
编译烧写后,执行命令:
set ipaddr 192.168.0.2
set serverip 192.168.0.100
save
然后reset,发现原来一直提示的一个警告Warning - bad CRC, using default environment已经没了,因为我们自己保存了环境变量。print一下,环境变量和我们设置的一样。
11.
之前我们的u-boot已经能够启动内核了,现在我们试试能不能挂载文件系统,首先先下载jffs2格式的文件系统到30000000。然后使用命令
nand write.jffs2 30000000 rootfs 59ad78烧写到nandflash的rootfs分区,在bootargs环境变量里添加rootfstype=jffs2,然后重启开发板,
发现内核能够挂载文件系统,证明我们的uboot支持烧写jffs2文件系统。
再试试yaffs2文件系统,同样下载到30000000,使用nand write.yaffs2 或nand write.yaffs发现都没有这个命令,于是我们进入nand的命令文件common/cmd_nand.c,
在do_nand函数里,有nand read或write的代码,而其中有对jffs2的支持,却并没有对yaffs2的支持。以前的老版本uboot是有对yaffs文件系统烧写的支持的,于是我们参考老版本
的uboot代码,在do_nand函数里的nand write/read部分加上一段代码,如下:
#ifdef CONFIG_CMD_NAND_TRIMFFS
} else if (!strcmp(s, ".trimffs")) {
if (read) {
printf("Unknown nand command suffix ‘%s‘\n", s);
return 1;
}
ret = nand_write_skip_bad(nand, off, &rwsize, NULL,
maxsize, (u_char *)addr,
WITH_DROP_FFS | WITH_WR_VERIFY);
#endif
#ifdef CONFIG_CMD_NAND_YAFFS
} else if (!strcmp(s, ".yaffs")) {
if (read) {
printf("Unknown nand command suffix ‘%s‘.\n", s);
return 1;
}
ret = nand_write_skip_bad(nand, off, &rwsize,NULL,//这里参数和老版比要修改下
maxsize,(u_char *)addr,
WITH_YAFFS_OOB);
#endif
在nand_help_text[]里添加nand write.yaffs的帮助信息:
"nand read.raw - addr off|partition [count]\n"
"nand write.raw - addr off|partition [count]\n"
" Use read.raw/write.raw to avoid ECC and access the flash as-is.\n"
#ifdef CONFIG_CMD_NAND_YAFFS
"nand write.yaffs - addr off|partition size\n"
" write ‘size‘ bytes starting at offset ‘off‘ with yaffs format\n"
" from memory address ‘addr‘, skipping bad blocks.\n"
#endif
nand_write_skip_bad函数内部也要修改:
if (actual)
*actual = 0;
#ifdef CONFIG_CMD_NAND_YAFFS
if (flags & WITH_YAFFS_OOB) {
if (flags & ~WITH_YAFFS_OOB)
return -EINVAL;
int pages;
pages = nand->erasesize / nand->writesize;
blocksize = (pages * nand->oobsize) + nand->erasesize;
if (*length % (nand->writesize + nand->oobsize)) {
printf ("Attempt to write incomplete page"
" in yaffs mode\n");
return -EINVAL;
}
} else
#endif
{
blocksize = nand->erasesize;
}
...
if (left_to_write < (blocksize - block_offset))
write_size = left_to_write;
else
write_size = blocksize - block_offset;
#ifdef CONFIG_CMD_NAND_YAFFS
if (flags & WITH_YAFFS_OOB) {
int page, pages;
size_t pagesize = nand->writesize;
size_t pagesize_oob = pagesize + nand->oobsize;
struct mtd_oob_ops ops;
ops.len = pagesize;
ops.ooblen = nand->oobsize;
ops.mode = MTD_OPS_RAW; //这里要改为RAW
ops.ooboffs = 0;
pages = write_size / pagesize_oob;
for (page = 0; page < pages; page++) {
WATCHDOG_RESET();
ops.datbuf = p_buffer;
ops.oobbuf = ops.datbuf + pagesize;
rval = nand->_write_oob(nand, offset, &ops);
if (rval != 0)
break;
offset += pagesize;
p_buffer += pagesize_oob;
}
}
else
#endif
{ //这里要加个左大括号
truncated_write_size = write_size;
#ifdef CONFIG_CMD_NAND_TRIMFFS
if (flags & WITH_DROP_FFS)
truncated_write_size = drop_ffs(nand, p_buffer,
&write_size);
#endif
rval = nand_write(nand, offset, &truncated_write_size,
p_buffer);
if ((flags & WITH_WR_VERIFY) && !rval)
rval = nand_verify(nand, offset,
truncated_write_size, p_buffer);
offset += write_size;
p_buffer += write_size;
} //这里要加个右大括号
...
同时,在include/nand.h中添加WITH_YAFFS_OOB宏的定义,
#define WITH_YAFFS_OOB (1 << 0)
最后在配置文件里添加CONFIG_CMD_NAND_YAFFS宏定义,编译烧写,成功烧写yaffs2文件系统。启动内核之前,去掉bootargs环境变量里之前添加的rootfstype=jffs2,挂载yaffs2不需要指定rootfstype。启动内核,挂载文件系统成功,此uboot已经支持yaffs2文件系统的烧写。
接下来 我们对uboot进行裁剪以缩小其大小,在配置文件中将一些不需要的功能的宏定义注释掉,然后编译,中间可能会有一些报错,根据提示解决即可。最后制作补丁,先make distclean删除编译内容,再使用如下命令制作补丁:
mv u-boot-2016.16 u-boot-2016.11_jz2440
tar -xjvf u-boot-2016.11.tar.bz2
diff -urN u-boot-2016.11 u-boot-2016.11_jz2440 > u-boot-2016.11_jz2440.patch
这样一个uboot就基本移植成功了。
make distclean;make smdk2440_config;make
usb 1 30000000
protect off all;erase 0 7ffff;cp.b 30000000 0 80000
以上是关于04.移植u-boot的主要内容,如果未能解决你的问题,请参考以下文章
uboot研读笔记 | 04 - 移植uboot 2012.04到JZ2440(支持Nor Flash读写)