免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 5888 | 回复: 7
打印 上一主题 下一主题

[BootLoader] 移植u-boot-2010.09到tq2440 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2011-06-08 21:10 |只看该作者 |倒序浏览
本帖最后由 hackqiang 于 2011-06-08 21:12 编辑

最近发现有许多人问有关u-boot移植的问题,小弟就抽空整了整以前做的u-boot移植文档,现在发在坛子上给大家看看,欢迎大家的批评指正,小弟不胜感激!



前言:
很久之前就买了块s3c2440的开发板tq2440,之前一直都是用天嵌提供的u-boot,但是这个u-boot的版本太老,使用的还是parameter_struct的传递内核参数的方法,这种传递参数的方法主要是2.6 之前的内核使用的方式。于是我就有了移植u-boot的想法,在网上看了不少u-boot移植的资料,但是大多都是讲到一些驱动的移植后就结束了,竟然连linux内核的引导都没有讲清楚,感觉很不完整,所以我打算再做一个文档作为补充。所以,我的移植重点在于完整的实现一个最最基本的bootloader的功能—从cpu加电开始,一直到bootloader完全的把cpu的控制权限交给内核,什么NOR Flash,网卡,统统用不到,所以不移植。

一.        移植的硬软件环境
移植版本:u-boot-2010.09
硬件参数:tq2440开发板,s3c2440A,64M的SDRAM,256M的nand Flash。

二.        U-boot引导内核的基本过程
接触过bootloader的人都知道bootloader是分为两个部分,即stage1和stage2,stage1的主要工作为:
1.        定义入口,设置异常向量;
2.        初始化硬件,如cpu,内存;
3.        将rom中的代码复制到ram中;
4.        初始化C的运行环境;
5.        跳转到ram中继续运行。
有了stage1的铺垫,stage2的工作就显得比较简单了:
1.        调用一些列的初始化函数,例如初始化nand flash;
2.        如果是交互模式,则进入主loop(),循环的接收执行用户输入的命令;
3.        如果不是交互模式,对于zImage格式的内核,就复制nand flash中的内核映像到ram的指定位置(如s3c2440为0x30008000),然后跳转到指定位置执行(这里的详细过程,以后会讲到),此刻,bootloader的使命就完成了,内核已经取得了cpu的完全控制。

三.        移植的准备工作
注意:cd ~/u-boot-2010.09表示进入u-boot源码的根目录。
1.        修改顶层目录的Makefile,增加tq2440的配置:
  1. tq2440_config:        unconfig
  2.                 @$(MKCONFIG) $@ arm arm920t tq2440 samsung s3c24x0
复制代码
同时修改160行为自己的交叉链,例如我的:
  1. CROSS_COMPILE ?=arm-linux-
复制代码
2.        因为2440与2410的差异并不大,所以通过复制s3c2410的源代码文件进行修改能大大减小移植的工作量:
  1. cd ~/u-boot-2010.09
  2. cp include/configs/smdk2410.h include/configs/tq2440.h
  3. cp -r board/samsung/smdk2410 board/samsung/tq2440
  4. mv board/samsung/tq2440/smdk2410.c board/samsung/tq2440/tq2440.c
复制代码
修改board/samsung/tq2440/Makefile 的28行
  1. COBJS        := smdk2410.o flash.o
复制代码
  1. COBJS        := tq2440.o flash.o
复制代码
3.        在arch/arm/cpu/arm920t/u-boot.lds的43行增加:
  1. board/samsung/tq2440/lowlevel_init.o (.text)
  2. board/samsung/tq2440/tq2440.o (.text)
复制代码
4.        测试准备工作
  1. make distclean
  2. make tq2440_config
  3. make
复制代码
如果不出问题的话,将会在u-boot的根目录生成一个u-boot.bin文件,这样,准备工作就完成了,下面就开始真正的移植啦!

四.        硬件相关修改
1.        中断部分的修改
修改arch/arm/cpu/arm920t/start.S的中断代码为:
  1. #define pWTCON        0x53000000
  2. #define INTMSK        0x4A000008
  3. #define INTSUBMSK        0x4A00001C
  4. #define CLKDIVN        0x4C000014
  5. ldr r1, =0x7fff
  6. ldr r0, =INTSUBMSK
  7. str r1, [r0]
复制代码
2.        cpu时钟设置部分的修改
我选择增加一个c函数clock_init的方式来修改系统时钟,因为用到了堆栈,所以将设置堆栈的代码剪切到前面,然后跳转到clock_init函数:
  1.         /* Set up the stack                                                    */
  2. stack_setup:
  3.         ldr        r0, _TEXT_BASE                /* upper 128 KiB: relocated uboot   */
  4.         sub        r0, r0, #CONFIG_SYS_MALLOC_LEN        /* malloc area              */
  5.         sub        r0, r0, #CONFIG_SYS_GBL_DATA_SIZE /* bdinfo                 */
  6. #ifdef CONFIG_USE_IRQ
  7.         sub        r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
  8. #endif
  9.         sub        sp, r0, #12                /* leave 3 words for abort-stack    */
  10.         bl        clock_init
复制代码
在board/samsung/tq2440/tq2440.c中删除掉board_init函数中有关时钟修改的代码,同时增加函数clock_init:
  1. static inline void delay (unsigned long loops)
  2. {
  3.         __asm__ volatile ("1:\n"
  4.           "subs %0, %1, #1\n"
  5.           "bne 1b":"=r" (loops):"0" (loops));
  6. }
  7. void clock_init(void)
  8. {
  9.         struct s3c24x0_clock_power *clk_power = (struct s3c24x0_clock_power *)0x4C000000;
  10.         #define S3C2440_CLKDIV                  0x05    /* FCLK:HCLK:PCLK = 1:4:8, UCLK = UPLL */
  11.         #define S3C2440_UPLL_48MHZ                ((0x38<<12)|(0x02<<4)|(0x02))
  12.         #define S3C2440_MPLL_400MHZ                ((0x5c<<12)|(0x01<<4)|(0x01))

  13.         clk_power->CLKDIVN = S3C2440_CLKDIV;

  14.         /* change to asynchronous bus mod */
  15.         __asm__(  "mrc   p15, 0, r1, c1, c0, 0\n" /* read ctrl register */  
  16.                     "orr   r1, r1, #0xc0000000\n" /* Asynchronous */  
  17.                      "mcr   p15, 0, r1, c1, c0, 0\n" /* write ctrl register  */
  18.                     :::"r1" );
  19.         /* to reduce PLL lock time, adjust the LOCKTIME register */
  20.         clk_power->LOCKTIME = 0xFFFFFF;
  21.         /* configure UPLL */
  22.         clk_power->UPLLCON = S3C2440_UPLL_48MHZ;                //fin=12.000MHz
  23.         /* some delay between MPLL and UPLL */
  24.         delay (4000);
  25.         /* configure MPLL */
  26.         clk_power->MPLLCON = S3C2440_MPLL_400MHZ;                //fin=12.000MHz
  27. }
复制代码
同时还需要修改arch/arm/cpu/arm920t/s3c24x0/speed.c:
67行修改为:
  1.         return (CONFIG_SYS_CLK_FREQ * m * 2) / (p << s);
复制代码
81行修改为:
  1.         return get_FCLK() / 4;
复制代码
85行修改为:
  1.         return get_FCLK() / 8 ;
复制代码
3.        把board/samsung/tq2440/tq2440.c中 的机器码为:
  1.         gd->bd->bi_arch_number = MACH_TYPE_TQ2440;
复制代码
4.        修改board/samung/tq2440/lowleve_init.S中的几个寄存器:
  1. #define DW8                        (0x0)
  2. #define DW16                        (0x1)
  3. #define DW32                        (0x2)
  4. #define WAIT                        (0x1<<2)
  5. #define UBLB                        (0x1<<3)

  6. #define B1_BWSCON                (DW16)
  7. #define B2_BWSCON                (DW16)
  8. #define B3_BWSCON                (DW16 + WAIT + UBLB)
  9. #define B4_BWSCON                (DW16)
  10. #define B5_BWSCON                (DW8)
  11. #define B6_BWSCON                (DW32)
  12. #define B7_BWSCON                (DW32)
复制代码
以及后面的:
  1. #define REFCNT                        0x4F4
复制代码
5.        测试本阶段的工作
注释掉start.S中的bl cpu_init_crit,即:
  1. @        bl        cpu_init_crit
复制代码
同时修改board/samsung/tq2440/config.mk 中的25行为:
  1. TEXT_BASE = 0x33000000         #0x33F80000
复制代码
编译好之后烧到开发板的ram中,能正常运行把!

评分

参与人数 1可用积分 +10 收起 理由
bitmilong + 10 精品文章

查看全部评分

论坛徽章:
0
2 [报告]
发表于 2011-06-08 21:13 |只看该作者
本帖最后由 hackqiang 于 2011-06-08 21:16 编辑

五.        Nand Flash驱动的移植
Nand flash的移植还是很重要的,因为我们的u-boot和内核镜像都是烧在nand flash中的,必须要能操作nand flash,才能继续进行下去(stage2),nand flash的驱动代码主要是取自天嵌的u-boot。
1.        建立文件drivers/mtd/nand/s3c2440_nand.c,添加如下内容:
  1. /*
  2. * Nand flash interface of s3c2440
  3. */
  4. #include <common.h>
  5. #if 0
  6. #define DEBUGN    printf
  7. #else
  8. #define DEBUGN(x, args ...) {}
  9. #endif
  10. #include <nand.h>
  11. #include <asm/arch/s3c24x0_cpu.h>
  12. #include <asm/io.h>
  13. #define __REGb(x)    (*(volatile unsigned char *)(x))
  14. #define __REGi(x)    (*(volatile unsigned int *)(x))

  15. #define NF_BASE  0x4e000000             //Nand配置寄存器基地址
  16. #define NFCONF   __REGi(NF_BASE + 0x0)  //偏移后还是得到配置寄存器基地址
  17. #define NFCONT   __REGi(NF_BASE + 0x4)  //偏移后得到Nand控制寄存器基地址
  18. #define NFCMD    __REGb(NF_BASE + 0x8)  //偏移后得到Nand指令寄存器基地址
  19. #define NFADDR   __REGb(NF_BASE + 0xc)  //偏移后得到Nand地址寄存器基地址
  20. #define NFDATA   __REGb(NF_BASE + 0x10) //偏移后得到Nand数据寄存器基地址
  21. #define NFMECCD0 __REGi(NF_BASE + 0x14) //偏移后得到Nand主数据区域ECC0寄存器基地址
  22. #define NFMECCD1 __REGi(NF_BASE + 0x18) //偏移后得到Nand主数据区域ECC1寄存器基地址
  23. #define NFSECCD  __REGi(NF_BASE + 0x1C) //偏移后得到Nand空闲区域ECC寄存器基地址
  24. #define NFSTAT   __REGb(NF_BASE + 0x20) //偏移后得到Nand状态寄存器基地址
  25. #define NFSTAT0  __REGi(NF_BASE + 0x24) //偏移后得到Nand ECC0状态寄存器基地址
  26. #define NFSTAT1  __REGi(NF_BASE + 0x28) //偏移后得到Nand ECC1状态寄存器基地址
  27. #define NFMECC0  __REGi(NF_BASE + 0x2C) //偏移后得到Nand主数据区域ECC0状态寄存器基地址
  28. #define NFMECC1  __REGi(NF_BASE + 0x30) //偏移后得到Nand主数据区域ECC1状态寄存器基地址
  29. #define NFSECC   __REGi(NF_BASE + 0x34) //偏移后得到Nand空闲区域ECC状态寄存器基地址
  30. #define NFSBLK   __REGi(NF_BASE + 0x38) //偏移后得到Nand块开始地址
  31. #define NFEBLK   __REGi(NF_BASE + 0x3c) //偏移后得到Nand块结束地址


  32. #define S3C2440_NFCONT_nCE  (1<<1)
  33. #define S3C2440_ADDR_NALE   0x0c
  34. #define S3C2440_ADDR_NCLE   0x08
  35. ulong IO_ADDR_W = NF_BASE;
  36. static void s3c2440_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
  37. {
  38.     struct nand_chip *chip = mtd->priv;

  39.     DEBUGN("hwcontrol(): 0x%02x 0x%02x\n", cmd, ctrl);

  40.     if (ctrl & NAND_CTRL_CHANGE) {
  41.         IO_ADDR_W = NF_BASE;

  42.         if (!(ctrl & NAND_CLE))                //要写的是地址
  43.             IO_ADDR_W |= S3C2440_ADDR_NALE;
  44.         if (!(ctrl & NAND_ALE))                //要写的是命令
  45.             IO_ADDR_W |= S3C2440_ADDR_NCLE;

  46.         if (ctrl & NAND_NCE)
  47.             NFCONT &= ~S3C2440_NFCONT_nCE;    //使能nand flash
  48.         else
  49.             NFCONT |= S3C2440_NFCONT_nCE;     //禁止nand flash
  50.     }

  51.     if (cmd != NAND_CMD_NONE)
  52.         writeb(cmd,(void *)IO_ADDR_W);
  53. }


  54. static int s3c2440_dev_ready(struct mtd_info *mtd)
  55. {
  56.     DEBUGN("dev_ready\n");
  57.     return (NFSTAT & 0x01);
  58. }

  59. int board_nand_init(struct nand_chip *nand)
  60. {
  61.     u_int32_t cfg;
  62.     u_int8_t tacls, twrph0, twrph1;
  63.     struct s3c24x0_clock_power * const clk_power = s3c24x0_get_base_clock_power();
  64.     DEBUGN("board_nand_init()\n");
  65.     clk_power->CLKCON |= (1 << 4);
  66.     twrph0 = 4; twrph1 = 2; tacls = 0;
  67.     cfg = (tacls<<12)|(twrph0<<8)|(twrph1<<4);
  68.     NFCONF = cfg;
  69.     cfg = (1<<6)|(1<<4)|(0<<1)|(1<<0);
  70.     NFCONT = cfg;
  71.    
  72.     /* initialize nand_chip data structure */
  73.     nand->IO_ADDR_R = nand->IO_ADDR_W = (void *)0x4e000010;
  74.     /* read_buf and write_buf are default */
  75.     /* read_byte and write_byte are default */
  76.     /* hwcontrol always must be implemented */
  77.     nand->cmd_ctrl = s3c2440_hwcontrol;
  78.     nand->dev_ready = s3c2440_dev_ready;
  79.     nand->ecc.mode = NAND_ECC_SOFT;
  80.     return 0;
  81. }
复制代码
2.        修改drivers/mtd/nand/Makefile,增加:
  1.                 COBJS-y += s3c2440_nand.o
复制代码
3.        修改include/config/tq2440.h,支持nand flash启动,随便把其他关于nand的设置一起做好:
  1. #define CONFIG_NAND_S3C2440 1
  2. #define CONFIG_CMD_NAND
  3. #define CONFIG_SYS_NAND_BASE  0x4E000000 //Nand配置寄存器基地址
  4. #define CONFIG_SYS_MAX_NAND_DEVICE      1  
  5. #define CONFIG_MTD_NAND_VERIFY_WRITE    1  
  6. #define CONFIG_ENV_IS_IN_NAND         1
  7. #define CONFIG_ENV_SIZE                        0x20000
  8. #define CONFIG_ENV_OFFSET                 0x40000
复制代码
4.        在arch/arm/include/asm/arch‐s3c24x0/s3c24x0.h中增加:
  1. struct s3c2440_nand {
  2.   u32 NFCONF;
  3.   u32 NFCONT;
  4.   u32 NFCMD;
  5.   u32 NFADDR;
  6.   u32 NFDATA;
  7.   u32 NFMECCD0;
  8.   u32 NFMECCD1;
  9.   u32 NFSECCD;
  10.   u32 NFSTAT;
  11.   u32 NFESTAT0;
  12.   u32 NFESTAT1;
  13.   u32 NFMECC0;
  14.   u32 NFMECC1;
  15.   u32 NFSECC;
  16.   u32 NFSBLK;
  17.   u32 NFEBLK;
  18. };       
复制代码
5.        在board/samsung/tq2440/tq2440.c中增加nand读写的函数:
  1. #define GSTATUS1        (*(volatile unsigned int *)0x560000B0)
  2. #define BUSY            1

  3. #define NAND_SECTOR_SIZE        512
  4. #define NAND_BLOCK_MASK        (NAND_SECTOR_SIZE - 1)

  5. #define NAND_SECTOR_SIZE_LP        2048
  6. #define NAND_BLOCK_MASK_LP        (NAND_SECTOR_SIZE_LP - 1)

  7. char bLARGEBLOCK;                        //HJ_add 20090807
  8. char b128MB;                                //HJ_add 20090807

  9. /* 供外部调用的函数 */
  10. void nand_init_ll(void);
  11. int nand_read_ll(unsigned char *buf, unsigned long start_addr, int size);
  12. int nand_read_ll_lp(unsigned char *buf, unsigned long start_addr, int size);

  13. static void nand_reset(void);
  14. static void wait_idle(void);
  15. static void nand_select_chip(void);
  16. static void nand_deselect_chip(void);
  17. static void write_cmd(int cmd);
  18. static void write_addr(unsigned int addr);
  19. static void write_addr_lp(unsigned int addr);
  20. static unsigned char read_data(void);
  21. int NF_ReadID(void);                                //HJ_add 20090807

  22. /* S3C2440的NAND Flash操作函数 */

  23. /* 复位 */
  24. static void nand_reset(void)
  25. {
  26.         nand_select_chip();
  27.         write_cmd(0xff);  // 复位命令
  28.         wait_idle();
  29.         nand_deselect_chip();
  30. }

  31. /* 等待NAND Flash就绪 */
  32. static void wait_idle(void)
  33. {
  34.         int i;
  35.         struct s3c2440_nand * s3c2440nand = (struct s3c2440_nand *)0x4e000000;
  36.         volatile unsigned char *p = (volatile unsigned char *)&s3c2440nand->NFSTAT;

  37.         while(!(*p & BUSY))
  38.         for(i=0; i<10; i++);
  39. }

  40. /* 发出片选信号 */
  41. static void nand_select_chip(void)
  42. {
  43.         int i;
  44.         struct s3c2440_nand * s3c2440nand = (struct s3c2440_nand *)0x4e000000;

  45.         s3c2440nand->NFCONT &= ~(1<<1);
  46.         for(i=0; i<10; i++);   
  47. }

  48. /* 取消片选信号 */
  49. static void nand_deselect_chip(void)
  50. {
  51.         struct s3c2440_nand * s3c2440nand = (struct s3c2440_nand *)0x4e000000;

  52.         s3c2440nand->NFCONT |= (1<<1);
  53. }

  54. /* 发出命令 */
  55. static void write_cmd(int cmd)
  56. {
  57.         struct s3c2440_nand * s3c2440nand = (struct s3c2440_nand *)0x4e000000;

  58.         volatile unsigned char *p = (volatile unsigned char *)&s3c2440nand->NFCMD;
  59.         *p = cmd;
  60. }

  61. /* 发出地址 */
  62. static void write_addr(unsigned int addr)
  63. {
  64.         int i;
  65.         struct s3c2440_nand * s3c2440nand = (struct s3c2440_nand *)0x4e000000;
  66.         volatile unsigned char *p = (volatile unsigned char *)&s3c2440nand->NFADDR;
  67.    
  68.         *p = addr & 0xff;
  69.         for(i=0; i<10; i++);
  70.         *p = (addr >> 9) & 0xff;
  71.         for(i=0; i<10; i++);
  72.         *p = (addr >> 17) & 0xff;
  73.         for(i=0; i<10; i++);
  74.         *p = (addr >> 25) & 0xff;
  75.         for(i=0; i<10; i++);
  76. }

  77. /* 发出地址 */
  78. static void write_addr_lp(unsigned int addr)
  79. {
  80.         int i;
  81.         struct s3c2440_nand * s3c2440nand = (struct s3c2440_nand *)0x4e000000;
  82.         volatile unsigned char *p = (volatile unsigned char *)&s3c2440nand->NFADDR;
  83.         int col, page;

  84.         col = addr & NAND_BLOCK_MASK_LP;
  85.         page = addr / NAND_SECTOR_SIZE_LP;
  86.          
  87.         *p = col & 0xff;                        /* Column Address A0~A7 */
  88.         for(i=0; i<10; i++);                 
  89.         *p = (col >> 8) & 0x0f;                /* Column Address A8~A11 */
  90.         for(i=0; i<10; i++);
  91.         *p = page & 0xff;                        /* Row Address A12~A19 */
  92.         for(i=0; i<10; i++);
  93.         *p = (page >> 8) & 0xff;        /* Row Address A20~A27 */
  94.         for(i=0; i<10; i++);
  95. if (b128MB == 0)
  96.         *p = (page >> 16) & 0x03;        /* Row Address A28~A29 */
  97.         for(i=0; i<10; i++);
  98. }

  99. /* 读取数据 */
  100. static unsigned char read_data(void)
  101. {
  102.         struct s3c2440_nand * s3c2440nand = (struct s3c2440_nand *)0x4e000000;
  103.         volatile unsigned char *p = (volatile unsigned char *)&s3c2440nand->NFDATA;
  104.         return *p;
  105. }


  106. /* 初始化NAND Flash */
  107. void nand_init_ll(void)
  108. {
  109.         struct s3c2440_nand * s3c2440nand = (struct s3c2440_nand *)0x4e000000;

  110.         #define TACLS   0
  111.         #define TWRPH0  3
  112.         #define TWRPH1  0

  113.         /* 设置时序 */
  114.         s3c2440nand->NFCONF = (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4);
  115.         /* 使能NAND Flash控制器, 初始化ECC, 禁止片选 */
  116.         s3c2440nand->NFCONT = (1<<4)|(1<<1)|(1<<0);

  117.         /* 复位NAND Flash */
  118.         nand_reset();
  119. }
  120. #if 1
  121. int NF_ReadID(void)
  122. {
  123.         char pMID;
  124.         char pDID;
  125.         int  nBuff;
  126.         char   n4thcycle;
  127.         int i;
  128.         struct s3c2440_nand * s3c2440nand = (struct s3c2440_nand *)0x4e000000;
  129.         volatile unsigned char *p = (volatile unsigned char *)&s3c2440nand->NFADDR;

  130.         b128MB = 1;
  131.         n4thcycle = nBuff = 0;

  132.         nand_init_ll();
  133.         nand_select_chip();
  134.         write_cmd(0x90);        // read id command
  135.         *p=0x00 & 0xff;
  136.         for ( i = 0; i < 100; i++ );

  137.         pMID = read_data();
  138.         pDID =  read_data();
  139.         nBuff =  read_data();
  140.         n4thcycle = read_data();

  141.         nand_deselect_chip();
  142.          
  143.         if (pDID >= 0xA0)
  144.         {
  145.                 b128MB = 0;
  146.         }

  147.         return (pDID);
  148. }
  149. #endif

  150. /* 读函数 */
  151. int nand_read_ll(unsigned char *buf, unsigned long start_addr, int size)
  152. {
  153.         int i, j;
  154.         char dat;
  155.         struct s3c2440_nand * s3c2440nand = (struct s3c2440_nand *)0x4e000000;
  156.         volatile unsigned char *p = (volatile unsigned char *)&s3c2440nand->NFADDR;

  157.    
  158.         if ((start_addr & NAND_BLOCK_MASK) || (size & NAND_BLOCK_MASK))
  159.         {
  160.                 return -1;    /* 地址或长度不对齐 */
  161.         }

  162.         /* 选中芯片 */
  163.         nand_select_chip();

  164.         for(i=start_addr; i < (start_addr + size);)
  165.         {
  166. /* Check Bad Block */
  167. if(1){
  168.                 /* 发出READ0命令 */
  169.                 write_cmd(0x50);

  170.                 *p = 5;
  171.                 for(j=0; j<10; j++);
  172.                 *p = (i >> 9) & 0xff;
  173.                 for(j=0; j<10; j++);
  174.                 *p = (i >> 17) & 0xff;
  175.                 for(j=0; j<10; j++);
  176.                 *p = (i >> 25) & 0xff;
  177.                 for(j=0; j<10; j++);
  178.                 wait_idle();

  179.                 dat = read_data();
  180.                 write_cmd(0);
  181.                  
  182.                 /* 取消片选信号 */
  183.                 nand_deselect_chip();
  184.                 if(dat != 0xff)
  185.                         i += 16384;                // 1 Block = 512*32= 16384
  186. /* Read Page */
  187.                 /* 选中芯片 */
  188.                 nand_select_chip();
  189. }
  190.                 /* 发出READ0命令 */
  191.                 write_cmd(0);

  192.                 /* Write Address */
  193.                 write_addr(i);
  194.                 wait_idle();

  195.                 for(j=0; j < NAND_SECTOR_SIZE; j++, i++)
  196.                 {
  197.                         *buf = read_data();
  198.                         buf++;
  199.                 }
  200.         }

  201.         /* 取消片选信号 */
  202.         nand_deselect_chip();

  203.         return 0;
  204. }

  205. /* 读函数
  206.   * Large Page
  207.   */
  208. int nand_read_ll_lp(unsigned char *buf, unsigned long start_addr, int size)
  209. {
  210.         int i, j;
  211.         char dat;
  212.         struct s3c2440_nand * s3c2440nand = (struct s3c2440_nand *)0x4e000000;
  213.         volatile unsigned char *p = (volatile unsigned char *)&s3c2440nand->NFADDR;

  214.         if ((start_addr & NAND_BLOCK_MASK_LP) || (size & NAND_BLOCK_MASK_LP))
  215.         {
  216.                 return -1;    /* 地址或长度不对齐 */
  217.         }

  218.         /* 选中芯片 */
  219.         nand_select_chip();

  220.         for(i=start_addr; i < (start_addr + size);)
  221.         {
  222. /* Check Bad Block */
  223. if(1){
  224.                 int col, page;

  225.                 col = i & NAND_BLOCK_MASK_LP;
  226.                 page = i / NAND_SECTOR_SIZE_LP;
  227.                 /* 发出READ0命令 */
  228.                 write_cmd(0x00);

  229.                 *p = 5;
  230.                 for(j=0; j<10; j++);
  231.                 *p = 8;
  232.                 for(j=0; j<10; j++);
  233.                 *p = page & 0xff;                /* Row Address A12~A19 */
  234.                 for(j=0; j<10; j++);
  235.                 *p = (page >> 8) & 0xff;                /* Row Address A20~A27 */
  236.                 for(j=0; j<10; j++);
  237. if (b128MB == 0)
  238.                 *p = (page >> 16) & 0x03;                /* Row Address A28~A29 */
  239.                 for(j=0; j<10; j++);

  240.                 write_cmd(0x30);
  241.                 wait_idle();

  242.                 dat = read_data();
  243.                  
  244.                 /* 取消片选信号 */
  245.                 nand_deselect_chip();
  246.                 if(dat != 0xff)
  247.                         i += 131072;                // 1 Block = 2048*64= 131072
  248. /* Read Page */
  249.                 /* 选中芯片 */
  250.                 nand_select_chip();
  251. }
  252.                 /* 发出READ0命令 */
  253.                 write_cmd(0);

  254.                 /* Write Address */
  255.                 write_addr_lp(i);
  256.                 write_cmd(0x30);
  257.                 wait_idle();

  258.                 for(j=0; j < NAND_SECTOR_SIZE_LP; j++, i++)
  259.                 {
  260.                         *buf = read_data();
  261.                         buf++;
  262.                 }
  263.         }

  264.         /* 取消片选信号 */
  265.         nand_deselect_chip();

  266.         return 0;
  267. }

  268. int bBootFrmNORFlash(void)
  269. {
  270.         volatile unsigned int *pdw = (volatile unsigned int *)0;
  271.         unsigned int dwVal;

  272.         /*
  273.          * 无论是从NOR Flash还是从NAND Flash启动,
  274.          * 地址0处为指令"b        Reset", 机器码为0xEA00000B,
  275.          * 对于从NAND Flash启动的情况,其开始4KB的代码会复制到CPU内部4K内存中,
  276.          * 对于从NOR Flash启动的情况,NOR Flash的开始地址即为0。
  277.          * 对于NOR Flash,必须通过一定的命令序列才能写数据,
  278.          * 所以可以根据这点差别来分辨是从NAND Flash还是NOR Flash启动:
  279.          * 向地址0写入一个数据,然后读出来,如果没有改变的话就是NOR Flash
  280.          */

  281.         dwVal = *pdw;      
  282.         *pdw = 0x12345678;
  283.         if (*pdw != 0x12345678)
  284.         {
  285.                 return 1;
  286.         }
  287.         else
  288.         {
  289.                 *pdw = dwVal;
  290.                 return 0;
  291.         }
  292. }

  293. int CopyCode2Ram(unsigned long start_addr, unsigned char *buf, int size)
  294. {
  295.         unsigned int *pdwDest;
  296.         unsigned int *pdwSrc;
  297.         int i;

  298.         if (bBootFrmNORFlash())
  299.         {
  300.                 pdwDest = (unsigned int *)buf;
  301.                 pdwSrc  = (unsigned int *)start_addr;
  302.                 /* 从 NOR Flash启动 */
  303.                 for (i = 0; i < size / 4; i++)
  304.                 {
  305.                         pdwDest[i] = pdwSrc[i];
  306.                 }
  307.                 return 0;
  308.         }
  309.         else
  310.         {
  311.                 /* 初始化NAND Flash */
  312.                 nand_init_ll();

  313.                 /* 从 NAND Flash启动 */
  314.                 if (NF_ReadID() == 0x76 )
  315.                         nand_read_ll(buf, start_addr, (size + NAND_BLOCK_MASK)&~(NAND_BLOCK_MASK));
  316.                 else
  317.                         nand_read_ll_lp(buf, start_addr, (size + NAND_BLOCK_MASK_LP)&~(NAND_BLOCK_MASK_LP));
  318.                 return 0;
  319.         }
  320. }
复制代码
6.        现在我们修改stage1的最后阶段:搬移u-boot代码到ram中,转到ram中继续执行。修改arch/arm/cpu/arm920t/start.S中代码搬运的部分:
  1.         sub        r2, r3, r2                /* r2 <- size of armboot  */
  2. #if 1
  3.         bl        CopyCode2Ram
  4. #else
  5.         add        r2, r0, r2                /* r2<- source end address   */

  6. copy_loop:
  7.         ldmia        r0!, {r3-r10}                /* copy from source address [r0]    */
  8.         stmia        r1!, {r3-r10}                /* copy to   target address [r1]    */
  9.         cmp        r0, r2                        /* until source end addreee [r2]    */
  10.         ble        copy_loop
复制代码
7.        恢复之前为了测试做的修改,将start.S中的bl cpu_init_crit的注释去掉,即:
  1.                 bl        cpu_init_crit
复制代码
同时修改board/samsung/tq2440/config.mk 中的25行为:
  1. TEXT_BASE = 0x33F80000
复制代码
编译后烧录到nand flash中,看看效果吧!

论坛徽章:
0
3 [报告]
发表于 2011-06-08 21:14 |只看该作者
六.        引导zImage
据我了解,u-boot本身是不支持直接引导zImage的,但是天嵌提供的u-boot就可以直接引导zImage,我就对源码研究了会,结合bootm命令的实现,发现原来引导zImage是如此的简单。
为了实现直接引导zImage,我添加了一个u-boot的命令boot_zImage,命令添加的方法到处都是,可以到这里看一看:http://qiang.ws/index.php?p=537。我主要说下这个命令的实现原理。
因为天嵌把nand flash分了三个区,内核映像就烧在第二个分区,第二个分区的起始地址为0x200000,所以从u-boot需要从nand flash的0x200000处读取内核文件,拷贝到SDRAM的0x30008000处,然后在地址gd->bd->bi_boot_params处设置传递给内核的参数,最后跳转到0x30008000执行,下面我贴出代码进行详细的说明:
  1. /*
  2. *        使用 tag list方式设置传递给内核的参数
  3. * pram_base: base address of linux paramter
  4. */
  5. static void setup_linux_param(long param_base)
  6. {
  7.         /* start of tags */
  8.         struct tag *params = (struct tag *)param_base;

  9.         params->hdr.tag = ATAG_CORE;
  10.         params->hdr.size = tag_size (tag_core);

  11.         params->u.core.flags = 0;
  12.         params->u.core.pagesize = 0;
  13.         params->u.core.rootdev = 0;

  14.         params = tag_next (params);


  15.         /* !!! importart set SDRAM */
  16.         params->hdr.tag = ATAG_MEM;
  17.         params->hdr.size = tag_size (tag_mem32);

  18.         params->u.mem.start = PHYS_SDRAM_1;
  19.         params->u.mem.size = PHYS_SDRAM_1_SIZE;

  20.         params = tag_next (params);
  21.        

  22.         /* set bootargs */
  23.         char *commandline = getenv ("bootargs");
  24.         if (!commandline)
  25.                 goto end;
  26.         params->hdr.tag = ATAG_CMDLINE;
  27.         params->hdr.size = (sizeof (struct tag_header) + strlen (commandline) + 1 + 4) >> 2;
  28.        
  29.         strcpy (params->u.cmdline.cmdline, commandline);

  30.         params = tag_next (params);


  31. end:
  32.         /* end of tags */
  33.         params->hdr.tag = ATAG_NONE;
  34.         params->hdr.size = 0;

  35. }



  36. /*
  37. * 将内核映像从nand flash拷贝到SDRAM中
  38. * dst: destination address
  39. * src: source
  40. * size: size to copy
  41. * mt: type of storage device
  42. */
  43. static inline int copy_kernel_img(ulong dst, const char *src, size_t size)
  44. {
  45.         int ret = 0;
  46.         if (NF_ReadID() == 0x76) {
  47.                 ret = nand_read_ll((unsigned char *)dst,
  48.                                    (unsigned long)src, (int)size);
  49.         } else {
  50.                 ret = nand_read_ll_lp((unsigned char *)dst,
  51.                                    (unsigned long)src, (int)size);
  52.         }
  53.         return ret;
  54. }


  55. int do_boot_zImage (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
  56. {
  57.         int ret;
  58.         ulong from=0x200000;        //这个是内核映像在nand flash中的其实地址
  59.         ulong to=0x30008000;         //内核在SDRAM中的起始地址
  60.         size_t size=0x300000;        //拷贝内核映像的大小


  61.         /* copy kernel image */
  62.         printf("Copy linux kernel from 0x%08lx to 0x%08lx, size = 0x%08lx ... ",
  63.                 from, to, size);
  64.         ret = copy_kernel_img(to, (char *)from, size);
  65.         if (ret) {
  66.                 printf("failed\n");
  67.                 return -1;
  68.         } else {
  69.                 printf("Copy Kernel to SDRAM done,");
  70.         }

  71. //这里进行魔数的判断,我觉得做不做无所谓,只是检查一下到底是不是zImage格式的映像而已
  72. #define LINUX_ZIMAGE_MAGIC                        0x016f2818       
  73.        
  74.         if (*(ulong *)(to + 9*4) != LINUX_ZIMAGE_MAGIC) {
  75.                 printf("Warning: this binary is not compressed linux kernel image\n");
  76.                 printf("zImage magic = 0x%08lx\n", *(ulong *)(to + 9*4));
  77.         } else {
  78.                 printf("zImage magic = 0x%08lx\n", *(ulong *)(to + 9*4));
  79.                 ;
  80.         }

  81.         printf("NOW, Booting Linux......\n");

  82.         /* set atag */
  83.         setup_linux_param(gd->bd->bi_boot_params);
  84.        
  85.         /* run kernel */
  86.         void(*kernel)(int zero, int arch, uint params);
  87.         kernel = (void(*)(int, int, uint))(to);
  88.         //跳转到0x30008000,这个传递了三个参数,分别是0,机器码和传递给内核的参数的地址,为什么是这三个参数呢?详情自己看内核的源码arch/arm/boot/compressed/head.S
  89. kernel(0, gd->bd->bi_arch_number,gd->bd->bi_boot_params);
  90.        
  91.         return 0;         
  92. }
复制代码
七.        总结
没有做移植之前移植认为bootloader很神秘,做完一遍后发现原来bootloader也很简单,并没有之前想象的那么难,希望本文档对你有所帮助,同时也希望大家给我指出不足和错误之处。

论坛徽章:
0
4 [报告]
发表于 2011-06-08 23:41 |只看该作者
int bBootFrmNORFlash(void)

{

        volatile unsigned int *pdw = (volatile unsigned int *)0;

        unsigned int dwVal;



        /*

         * 无论是从NOR Flash还是从NAND Flash启动,

         * 地址0处为指令"b        Reset", 机器码为0xEA00000B,

         * 对于从NAND Flash启动的情况,其开始4KB的代码会复制到CPU内部4K内存中,

         * 对于从NOR Flash启动的情况,NOR Flash的开始地址即为0。

         * 对于NOR Flash,必须通过一定的命令序列才能写数据,

         * 所以可以根据这点差别来分辨是从NAND Flash还是NOR Flash启动:

         * 向地址0写入一个数据,然后读出来,如果没有改变的话就是NOR Flash

         */



        dwVal = *pdw;      

        *pdw = 0x12345678;

        if (*pdw != 0x1234567

        {

                return 1;

        }

        else

        {

                *pdw = dwVal;

                return 0;

        }

}


对于这一点,我不认同!

     当然也可以通过另外一种方法判断是从Norflash启动,还是从Nandflash启动。查看存储器的地址空间分布图,从Norflash启动,内部的4K RAM被映射到了0x40000000的地方;而从Nandflash启动,内部的4K RAM同时被映射到了0x40000000和0x00000000两个地方。如果在0x40000000~0x40001000的某个地址写入一个0数据,然后读出0x00000000~0x00001000相应位置的数据是否为0,是的话则是从Nandflash启动,示例代码如下。
int bBootFrmNORFlash(void)
{
        volatile unsigned int *pdw=(volatile unsigned int *)0x40000000;
volatile unsigned int *pdw0=(volatile unsigned int *)0x0;
        unsigned int dwVal;
        dwVal=*pdw0;//从0地址处读取数据
        *pdw=0x12345678; //向0地址处写入数据
        if(*pdw0!=0x1234567//如果不能够将数据写入到Norflash中,表示CPU是从Norflash启动的
        {
                return 1;
        }
        else
        {
                *pdw0=dwVal;
                return 0;
        }       
}

论坛徽章:
0
5 [报告]
发表于 2011-06-09 00:06 |只看该作者

论坛徽章:
0
6 [报告]
发表于 2011-06-09 11:08 |只看该作者
本帖最后由 hackqiang 于 2011-06-09 11:11 编辑

回复 4# txgc_wm


    txgc_wm兄,你给的地址我看了,受益匪浅啊。其实我对nand flash和nor flash的读取并不是很清楚,但是事实证明上面那段代码是可以正常使用的,他是我戒指从天嵌的u-boot中拿出来的。

    其实我还是偷懒了,既然我只支持从nand flash启动,我还判断什么呢?

论坛徽章:
0
7 [报告]
发表于 2011-06-09 19:26 |只看该作者
回复 6# hackqiang


    呵呵,有机会一起探讨,我买的也是天嵌的开发板!

论坛徽章:
0
8 [报告]
发表于 2011-06-10 16:11 |只看该作者
学习了
收藏
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP