在此首先要特别感谢亚嵌的李明老师和Alex Ling对我无私的帮助和支持。
这次更新,主要实现了Nand启动,并修改了前几个版本的几个小bug。ver3.0已经基本完成了u-boot的主线功能。后面我还会继续更新其它辅助功能。
之前上传的几个版本,对nandflash烧写时ECC校验是基于软件ECC,由于S5PV210的IROM中固化的启动代码(暂且称其为BL0)在读nandflash时
用的是8bit 硬件ECC。因此在烧写u-boot for tiny210到nandflash时,需要用开启了8bit 硬件ECC的 SD卡启动的u-boot 进行烧写(详见后面分析)。
您可以从下面的链接获得源码,也可以下载历史版本,并参考后面的步骤修改获得ver3.0.
ver3.0源码下载:u-boot for tiny 210 ver3.0
您也可以打开下面的链接阅读ver3.0源码
Git source tree(这是我在Gitorious建的第一个工程,对它的管理还是显的比较混乱。等我整理好另一个clone的工程,我会把链接贴上来)。
下面的链接提供了历史版本的源码
ver2.2源码下载: u-boot for tiny210 ver2.2
ver2.1源码下载:u-boot for tiny210 ver2.2
ver2.1源码下载:u-boot for tiny210 ver2.1
ver2.0源码下载:u-boot for tiny210 ver2.0
ver3.0的基本功能:
1. SD boot,基于linaro u-boot的SPL功能实现
2. 从SD卡的FAT分区上加载文件到SDRAM
3. 将环境变量保存至SD卡
4. 添加DM9000网卡驱动,开启网络功能(例如:tftp,nfs等)
5. 添加TAB键命令自动补全功能
6.修复bug:
修复bug 1:SD卡保存环境变量出现Writing to MMC(0)... mmc_send_cmd: error during transfer: 0x00208001 mmc write failed。
修复bug 2:每次启动只能保存一次环境变量。
7.添加NandFlash驱动,开启所有Nand cmd。
8.添加Yaffs文件系统烧写支持。
+9.修改在SD卡启动时对nandflash的烧写为8bit 硬件ECC校验。(nand启动仍为软件ECC)
The booting sequence in internal ROM is as follows:
1. Disable the watchdog timer.
3. Initialize the stack and heap region.
4. Check secure key.
6. Check OM pin and load the first boot loader (The size of boot loader depends on S/W) from specific device (block number 0) to iRAM.
7. If secure booting is successful, execute integrity check
8. If integrity check passes, then jump to the first boot loader in iRAM (0xD002_0010)
The booting sequence in internal SRAM is as follows:
1. Load the second boot loader from boot device to iRAM.
2. If secure booting is successful, execute integrity check.
3. If integrity check passes, then jump to the second boot loader in iRAM (The jumping address depends on user's software)
4. If integrity check fails, then stop the first boot loader.
5. The second boot loader Initializes the DRAM controller.
6. Load the OS image from specific device (block number 1) to DRAM.
7. Jump to OS code in DRAM (0x2000_0000 or 0x4000_0000)
for(i = 0, checksum = 0; i < IMG_SIZE - SPL_HEADER_SIZE; i++)
checksum += (0x000000FF) & *a++;
-/* FLASH and environment organization */
-#define CONFIG_SYS_NO_FLASH 1
-#undef CONFIG_CMD_IMLS
-#define CONFIG_IDENT_STRING " for FriendlyLEG-TINY210"
-
-#define CONFIG_ENV_IS_IN_MMC 1
-#define CONFIG_SYS_MMC_ENV_DEV 0
-#define CONFIG_ENV_SIZE 0x4000 /* 16KB */
-#define RESERVE_BLOCK_SIZE (512)
-#define BL1_SIZE (8 << 10) /*8 K reserved for BL1*/
-#define CONFIG_ENV_OFFSET (RESERVE_BLOCK_SIZE + BL1_SIZE + ((16 + 512) * 1024))
-#define CONFIG_DOS_PARTITION 1
然后在
460:
+/* FLASH and environment organization */
+#define CONFIG_SYS_NO_FLASH 1
+#undef CONFIG_CMD_IMLS
+#define CONFIG_IDENT_STRING " for FriendlyLEG-TINY210"
+#if 1
+#define CONFIG_TINY210_NAND_BOOT 1
+#endif
+#if 0
+#define CONFIG_TINY210_MMC_BOOT 1
+#endif
+/*MMC BOOT */
+#if defined(CONFIG_TINY210_MMC_BOOT)
+#define CFG_NAND_HWECC
+#define CONFIG_NAND_BL1_8BIT_ECC
+#define CONFIG_ENV_IS_IN_MMC 1
+#define CONFIG_SYS_MMC_ENV_DEV 0
+#define CONFIG_ENV_SIZE 0x4000 /* 16KB */
+#define RESERVE_BLOCK_SIZE (512)
+#define BL1_SIZE (8 << 10) /*8 K reserved for BL1*/
+#define CONFIG_ENV_OFFSET (RESERVE_BLOCK_SIZE + BL1_SIZE + ((16 + 512) * 1024))
+#endif
+
+#define CONFIG_DOS_PARTITION 1
+
+/*NAND_BOOT by lk */
+#if defined(CONFIG_TINY210_NAND_BOOT)
+#define CONFIG_S5PC11X
+#define CONFIG_ENV_IS_IN_NAND 1
+#define CONFIG_ENV_SIZE 0x4000 /* 16KB */
+#define CONFIG_ENV_OFFSET 0x40000
+#endif
#define CONFIG_TINY210_NAND_BOOT 1
即可实现nand启动
#define CONFIG_TINY210_MMC_BOOT 1
即可实现sd启动
2.修改board/samsung/tiny210/Makefile 即可通过定义CONFIG_TINY210_MMC_BOOT/CONFIG_TINY210_NAND_BOOT,分别实现SD启动和NAND启动。
ifdef CONFIG_SPL_BUILD
+
+ifdef CONFIG_TINY210_MMC_BOOT
COBJS += mmc_boot.o
endif
+ifdef CONFIG_TINY210_NAND_BOOT
COBJS += nand_cp.o
+endif
+endif
屏蔽掉38行的COBJS += nand_cp.o
3.修改实现8bit 硬件ECC校验。board/samsung/tiny210/nand.c中
44:static struct nand_ecclayout s3c_nand_oob_16 = {
.eccbytes = 4,
.eccpos = {1, 2, 3, 4},
.oobfree = {
{.offset = 6,
. length = 10}}
};
-
52:+static struct nand_ecclayout s3c_nand_oob_64_8bit = {
+ .eccbytes = 52,
+ .eccpos = {
+ 12,13,14,15,
+ 16,17,18,19,20,21,22,23,
+ 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39,
+ 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55,
+ 56, 57, 58, 59, 60, 61, 62, 63},
+ .oobfree = {
+ {.offset = 2,
+ .length = 10}}
+};
/* Nand flash oob definition for SLC 2k page size by jsgood */
static struct nand_ecclayout s3c_nand_oob_64 = {
屏蔽掉下面两个函数中的while
上面的信息可见【25】【24】位是对MLC nandflash的状态位,而tiny210上用的是SLC的nandflash。所以进行下面的屏蔽。
static void s3c_nand_wait_enc(void)
{
+// while (!(readl(NFECCSTAT) & NFSTAT_ECCENCDONE)) {}
}
/*
@@ -187,7 +200,7 @@ static void s3c_nand_wait_enc(void)
*/
static void s3c_nand_wait_dec(void)
{
+// while (!(readl(NFECCSTAT) & NFSTAT_ECCDECDONE)) {}
}
@@ -1042,9 +1067,24 @@
int board_nand_init(struct nand_chip *nand)
nand_type = S3C_NAND_TYPE_SLC;
nand->ecc.size = 512;
nand->ecc.bytes = 4;
-
1060:+
+ if ((1024 << (tmp & 3)) == 4096) {
+ /* Page size is 4Kbytes */
+ nand->ecc.read_page = s3c_nand_read_page_8bit;
+ nand->ecc.write_page = s3c_nand_write_page_8bit;
+ nand->ecc.read_oob = s3c_nand_read_oob_8bit;
+ nand->ecc.write_oob = s3c_nand_write_oob_8bit;
+ nand->ecc.layout = &s3c_nand_oob_128;
+ nand->ecc.hwctl = s3c_nand_enable_hwecc_8bit;
+ nand->ecc.calculate = s3c_nand_calculate_ecc_8bit;
+ nand->ecc.correct = s3c_nand_correct_data_8bit;
+ nand->ecc.size = 512;
+ nand->ecc.bytes = 13;
+ nand->options |= NAND_NO_SUBPAGE_WRITE;}
+ else
if ((1024 << (tmp & 0x3)) > 512) {
- nand->ecc.read_page = s3c_nand_read_page_1bit;
1073:+#if defined(CONFIG_NAND_4BIT_ECC)
+ nand->ecc.read_page = s3c_nand_read_page_1bit;
nand->ecc.write_page = s3c_nand_write_page_1bit;
nand->ecc.read_oob = s3c_nand_read_oob_1bit;
nand->ecc.write_oob = s3c_nand_write_oob_1bit;
@@ -1053,6 +1093,18 @@ int board_nand_init(struct nand_chip *nand)
nand->ecc.calculate = s3c_nand_calculate_ecc;
nand->ecc.correct = s3c_nand_correct_data;
nand->options |= NAND_NO_SUBPAGE_WRITE;
1083:+#endif
+nand->ecc.read_page = s3c_nand_read_page_8bit;
+ nand->ecc.write_page = s3c_nand_write_page_8bit;
+ nand->ecc.read_oob = s3c_nand_read_oob_8bit;
+ nand->ecc.write_oob = s3c_nand_write_oob_8bit;
+ nand->ecc.layout = &s3c_nand_oob_64_8bit;
+ nand->ecc.hwctl = s3c_nand_enable_hwecc_8bit;
+ nand->ecc.calculate = s3c_nand_calculate_ecc_8bit;
+ nand->ecc.correct = s3c_nand_correct_data_8bit;
+ nand->ecc.size = 512;
+ nand->ecc.bytes = 13;
+ nand->options |= NAND_NO_SUBPAGE_WRITE;
} else {
nand->ecc.layout = &s3c_nand_oob_16;
}
4,修改nand_cp.c文件。
+//#include <regs.h>
+#include <s5pc110.h>
+#define COPY_BL2_SIZE 0x80000
@@ -98,7 +98,7 @@ static int nandll_read_blocks (ulong dst_addr, ulong size, int large_block)
page_shift = 11;
/* Read pages */
98: for (i =(0x6000>>page_shift); i < (size>>page_shift); i++, buf+=(1<<page_shift)) {
nandll_read_page(buf, i, large_block);
}
0x6000是24k后的第一个地址,也就是从这个nandflash的这个偏移地址开始cp内容到DRAM。
@@ -128,8 +128,29 @@ int copy_uboot_to_ram (void)
128:+ return nandll_read_blocks(CONFIG_SYS_TEXT_BASE, COPY_BL2_SIZE, large_block);
+}
+void board_init_f(unsigned long bootflag)
+{
+ __attribute__((noreturn)) void (*uboot)(void);
+ copy_uboot_to_ram();
+
+ /* Jump to U-Boot image */
+ uboot = (void *)CONFIG_SYS_TEXT_BASE;
+ //while(1);
+ (*uboot)();
+ /* Never returns Here */
}
-
+
+/* Place Holders */
+void board_init_r(gd_t *id, ulong dest_addr)
+{
+ /* Function attribute is no-return */
+ /* This Function never executes */
+ while (1)
+ ;
+}
+
+void save_boot_params(u32 r0, u32 r1, u32 r2, u32 r3) {}
5.修改board/samsung/tiny210/tools/mkv210_image.c
8:#define BUFSIZE (24*1024)
#define IMG_SIZE (24*1024)
这个参数的修改是为了便于实现nandboot和SDboot的切换,因为
在board/samsung/tiny210/mmc_boot.c第四十九行可以找到
#define MOVI_BL2_POS ((eFUSE_SIZE / MOVI_BLKSIZE) + MOVI_BL1_BLKCNT + MOVI_ENV_BLKCNT)
当sdboot的BL1阶段要从MOVI_BL2_POS(24k + 1k(sd卡的第0扇区))这个偏移位置开始向DRAM cp代码。虽然启动时只会从nand或SD卡一次性读取16K内容,但是为了统一两种启动方式tiny210-spl.bin的大小,生成24k大小的tiny210-spl.bin(这并不会影响校验和,因为文件尾是由0填充)。
另外我在源码的根目录下写了一个简单的脚本make-tiny210-boot.sh,用于合并tiny210-spl.bin和u-boot.bin文件->生成tiny210-uboot.bin。这样我们以后烧写uboot时就不用像老版本那样分别烧写两个文件了。
至此ver3,0修改完成.
$make ARCH=arm CROSS_COMPILE=/opt/FriendlyARM/toolschain/4.5.1/bin/arm-none-linux-gnueabi- tiny210_config
$make ARCH=arm CROSS_COMPILE=/opt/FriendlyARM/toolschain/4.5.1/bin/arm-none-linux-gnueabi- all spl
由于我的系统下装有两套交叉工具链,所以没有把 /opt/FriendlyARM/toolschain/4.5.1/bin/ 添加到环境变量,在使用工具链时要指明路径。
1.sd启动
将SD卡通过读卡器接上电脑(或直接插入笔记本卡槽),通过"cat /proc/partitions"找出SD卡对应的设备,我的设备节点是/dev/sdb.
执行下面的命令
$sudo dd iflag=dsync oflag=dsync if=tiny210-uboot.bin of=/dev/sdb seek=1
对于BCH算法目前通常以512Byte或者1024Byte为单位处理,因为BCH按位处理数据,所以是4096bit或者8192bit,这里的4096/8192bit是原始数据,BCH需要生成一定的校验数据。下面简要介绍下原理
设最大纠错能力为t
如果选用4096bit的原始数据长度,则模式为BCH(8191,8191-13×t,t,13)
如果选用8192bit的原始数据长度,则模式为BCH(16383,16383-14×t,t,14)
校验数据长度就是13×t,或者14×tbit
所以平均1024+32Byte的MLC 大多建议使用8bit/512Byte ECC
平均1024+45Byte的MLC大多建议使用24Bit/1024Byte ECC, 此时需要14×24bit=42Byte的检验数据空间
以8bit/512Byte BCH方式的ECC为例,控制器写数据到NAND Flash时,每512Byte数据经过BCH模块就会生成13Byte的校验数据(当然剩下的16-13=3Byte也可以作为某种用途的数据,可以任意使用0-3Byte而不会改变ECC的使用),这些数据一起写入到NAND Flash中。控制器从NAND Flash中读取数据的时候需要将原始数据和校验数据一起读出经过BCH模块,BCH模块计算伴随矩阵首先可以判断出是否出现了错误,如果出现了错误需要计算错误位置多项式,然后解多项式,得到错误位置(目前主要使用Chien-search方法),因为是位错误,找到错误的位置以后取反以后就是正确的数据。只要是错误个数小于等于8,BCH都能够找到错误的位置,但是如果错误个数超过了8,对于BCH来说已经没有办法纠正错误了,只能报告出现了不可以纠正的情况。