Chinaunix首页 | 论坛 | 博客
  • 博客访问: 54170
  • 博文数量: 16
  • 博客积分: 125
  • 博客等级: 入伍新兵
  • 技术积分: 131
  • 用 户 组: 普通用户
  • 注册时间: 2011-04-12 08:47
文章分类

全部博文(16)

文章存档

2014年(1)

2013年(1)

2012年(3)

2011年(11)

我的朋友

分类:

2011-09-17 13:35:47

原文地址:U-boot启动详解(转) 作者:gaofei8530

cpu/arm920t/start.S
/*
 *************************************************************************
 *
 * Jump vector table as in table 3.1 in [1]
 *
 *************************************************************************
 */
;定义变量_start,然后跳转到处理器复位代码
.globl _start
_start: b       reset
;产生中断则利用pc来跳转到对应的中断处理程序中
 ldr pc, _undefined_instruction
 ldr pc, _software_interrupt
 ldr pc, _prefetch_abort
 ldr pc, _data_abort
 ldr pc, _not_used
 ldr pc, _irq
 ldr pc, _fiq
;利用.word来在当前位置放置一个值,这个值实际上就用对应的中断处理函数的地址
;.word的意义为在当前地址处放入一个16bits值
_undefined_instruction: .word undefined_instruction
_software_interrupt: .word software_interrupt
_prefetch_abort: .word prefetch_abort
_data_abort:  .word data_abort
_not_used:  .word not_used
_irq:   .word irq
_fiq:   .word fiq
 .balignl 16,0xdeadbeef

/*
 *************************************************************************
 *
 * Startup Code (reset vector)
 *
 * do important init only if we don't start from memory!
 * relocate armboot to ram
 * setup stack
 * jump to second stage
 *
 *************************************************************************
 */
;定义变量
_TEXT_BASE:
 .word TEXT_BASE
.globl _armboot_start
_armboot_start:
 .word _start
/*
 * These are defined in the board-specific linker script.
 */
.globl _bss_start
_bss_start:
 .word __bss_start
.globl _bss_end
_bss_end:
 .word _end
#ifdef CONFIG_USE_IRQ
/* IRQ stack memory (calculated at run-time) */
.globl IRQ_STACK_START
IRQ_STACK_START:
 .word 0x0badc0de
/* IRQ stack memory (calculated at run-time) */
.globl FIQ_STACK_START
FIQ_STACK_START:
 .word 0x0badc0de
#endif

/*
 * the actual reset code
 */
;实际处理代码
reset:
 /*
  * set the cpu to SVC32 mode
  */
 mrs r0,cpsr
 ;bic清除指定为1的位
 bic r0,r0,#0x1f
 ;orr逻辑或操作
 orr r0,r0,#0xd3
 ;经过以上两步r0值控制位位11010011,第0~4位标识处理器当前所处模式为10011(32位管理模式),第6、7位
 ;为1标识禁止IRQ和FIQ中断,第5位为0标识程序运行为arm状态,若其为1则运行在thumb状态
 ;设置处理器为32位管理模式,并运行与arm状态
 msr cpsr,r0
/* turn off the watchdog */
#if defined(CONFIG_S3C2400)
# define pWTCON  0x15300000
# define INTMSK  0x14400008 /* Interupt-Controller base addresses */
# define CLKDIVN 0x14800014 /* clock divisor register */
#elif defined(CONFIG_S3C2410)
;看门狗寄存器地址
# define pWTCON  0x53000000
;中断掩码寄存器,决定那个中断源被屏蔽,某位为1则屏蔽中断源,初始值为0xFFFFFFFF,屏蔽所有中断
# define INTMSK  0x4A000008 /* Interupt-Controller base addresses */
;中断子掩码寄存器,该寄存器只能屏蔽11个中断源,因此其仅低11位有效,初始值为0x7FF
# define INTSUBMSK 0x4A00001C
;时钟分频控制寄存器
# define CLKDIVN 0x4C000014 /* clock divisor register */
#endif
#if defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410)
 ;将看门狗寄存器清空,其各位含义为,第0位为1则当看门狗定时器溢出时重启,为0则不重启,初值为1
 ;第2位为中断使能位,初值为0
 ;第3、4位为时钟分频因子,初值为00,
 ;第5位为看门狗的使能位初值为1
 ;第8~15位为比例因子,初值为0x80
 ldr     r0, =pWTCON
 mov     r1, #0x0
 ;将看门狗寄存器所有位置零,关闭看门狗,其实只要将第5位置0即可
 str     r1, [r0]
 ;屏蔽所有中断,实际上中断屏蔽掩码寄存器初值即为0xFFFFFFF
 mov r1, #0xffffffff
 ldr r0, =INTMSK
 str r1, [r0]
# if defined(CONFIG_S3C2410)
 ;设置子中断掩码寄存器
 ldr r1, =0x3ff
 ldr r0, =INTSUBMSK
 str r1, [r0]
# endif
 ;设置时钟寄存器,CLKDIVN第0位为PDIVN,为0则PCLK=HCLK,为1则PCLK=HCLK/2
 ;第1位为HDIVN,为0则HCLK=FCLK,为1则HCLK=FCLK/2
 ;这里两位均为1,则FCLK:HCLK:PCLK = 4:2:1
 /* default FCLK is 120 MHz ! */
 ldr r0, =CLKDIVN
 mov r1, #3
 str r1, [r0]
#endif /* CONFIG_S3C2400 || CONFIG_S3C2410 */
 /*
  * we do sys-critical inits only at reboot,
  * not when booting from ram!
  */
  ;对临界寄存器的初始化,如果从ram中启动则不执行,如果重启则执行
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
 bl cpu_init_crit
#endif
;重定向代码,也就是从flash中复制到ram中
#ifndef CONFIG_SKIP_RELOCATE_UBOOT
relocate:    /* relocate U-Boot to RAM     */
 ;当前代码地址,adr获取当前代码的地址信息,若从ram运行则_start=TEXT_BASE,否则_start=0x00000000
 adr r0, _start  /* r0 <- current position of code   */
 ;获取_TEXT_BASE
 ldr r1, _TEXT_BASE  /* test if we run from flash or RAM */
 cmp     r0, r1                  /* don't reloc during debug         */
 ;两者相等,表示从ram运行则跳转到堆栈设置
 beq     stack_setup
 ;不相等则表示从flash中运行,重定向代码
 ldr r2, _armboot_start
 ;获取未初始化数据段地址
 ldr r3, _bss_start
 ;计算代码段大小
 sub r2, r3, r2  /* r2 <- size of armboot            */
 ;计算代码段终止地址
 add r2, r0, r2  /* r2 <- source end address         */
 ;复制代码,r0为代码的起始地址,r1为ram中地址,r2为代码的终止地址
 ;每次copy后将r0值递增同r2比较来判断是否复制完成
copy_loop:
 ldmia r0!, {r3-r10}  /* copy from source address [r0]    */
 stmia r1!, {r3-r10}  /* copy to   target address [r1]    */
 cmp r0, r2   /* until source end addreee [r2]    */
 ble copy_loop
#endif /* CONFIG_SKIP_RELOCATE_UBOOT */
 /* Set up the stack          */
stack_setup:
 ;获取_TEXT_BASE
 ldr r0, _TEXT_BASE  /* upper 128 KiB: relocated uboot   */
 ;获取分配区域起始指针,CFG_MALLOC_LEN=128*1024+CFG_ENV_SIZE=128*1024+0x10000=192K
 sub r0, r0, #CFG_MALLOC_LEN /* malloc area                      */
 ;另外分配128bytes来存储开发板信息
 sub r0, r0, #CFG_GBL_DATA_SIZE /* bdinfo                        */
#ifdef CONFIG_USE_IRQ
 sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
#endif
 ;再减去12bytes用于栈起点
 sub sp, r0, #12  /* leave 3 words for abort-stack    */
 ;清空未初始化数据段
clear_bss:
 ldr r0, _bss_start  /* find start of bss segment        */
 ldr r1, _bss_end  /* stop here                        */
 mov  r2, #0x00000000  /* clear                            */
clbss_l:str r2, [r0]  /* clear loop...                    */
 add r0, r0, #4
 cmp r0, r1
 ble clbss_l
#if 0
 ;关闭看门狗
 /* try doing this stuff after the relocation */
 ldr     r0, =pWTCON
 mov     r1, #0x0
 str     r1, [r0]
 /*
  * mask all IRQs by setting all bits in the INTMR - default
  */
 ;禁止中断
 mov r1, #0xffffffff
 ldr r0, =INTMR
 str r1, [r0]
 ;设置时钟
 /* FCLK:HCLK:PCLK = 1:2:4 */
 /* default FCLK is 120 MHz ! */
 ldr r0, =CLKDIVN
 mov r1, #3
 str r1, [r0]
 /* END stuff after relocation */
#endif
 ;完成复制后跳转到start_armboot,到这里就进入函数lib_arm/board.c的start_armboot函数中
 ldr pc, _start_armboot
_start_armboot: .word start_armboot
;这里指的从flash中运行是指的从flash rom中运行,也就是常说的从nor flash中运行程序,现在有很多开发板;都是利用SDRAM和NAND-FLASH共同工作,所以需要添加从nand flash启动的代码。在ldr pc,_start_armboot前;;添加如下代码:
#ifdef CONFIG_S3C2410_NAND_BOOT
 bl    copy_myself
    
 @ jump to ram
 ldr   r1, =on_the_ram
 add  pc, r1, #0
 nop
 nop
1:  b     1b          @ infinite loop
       
on_the_ram:
#endif
;开始就跳转到函数copy_myself中,
#ifdef CONFIG_S3C2410_NAND_BOOT
copy_myself:
 ;保存断点地址
 mov r10, lr
@ reset NAND
 ;NAND_CTRL_BASE为nand flash的寄存器基址为0x4E000000
 ;根据flash手册来复位flash
 mov r1, #NAND_CTL_BASE
 ;通过nand flash配置寄存器配置nand flash,该寄存器仅低16位有效
 ;第15位为nand flash控制器使能位,第12位为初始化ECC使能位,第11位为nand falsh memory nFCE使能
 ldr   r2, =0xf830           @ initial value
 str   r2, [r1, #oNFCONF]
 ldr   r2, [r1, #oNFCONF]
 ;将第11位清零,使能芯片
 bic  r2, r2, #0x800         @ enable chip
 str   r2, [r1, #oNFCONF]
 ;写入命令
 mov r2, #0xff               @ RESET command
 strb r2, [r1, #oNFCMD]
 ;循环延时
 mov r3, #0                  @ wait
1: add  r3, r3, #0x1
 cmp r3, #0xa
 blt   1b
 ;利用状态寄存器测试flash内部操作是否完成,如果完成则其状态寄存器将返回1
 ;等待flash操作完成
2: ldr   r2, [r1, #oNFSTAT]    @ wait ready
 tst    r2, #0x1
 beq  2b
 ;禁止芯片
 ldr   r2, [r1, #oNFCONF]
 orr  r2, r2, #0x800         @ disable chip
 str   r2, [r1, #oNFCONF]
  @ get read to call C functions (for nand_read())
  ;建立堆栈,栈起点为0x33f00000,大小为0x8000
 ldr   sp, DW_STACK_START    @ setup stack pointer
 mov fp, #0                  @ no previous frame, so fp=0
  @ copy U-BOOT to RAM
  ;UBOOT_RAM_BASE应该同TEXT_BASE相同
 ldr   r0, =UBOOT_RAM_BASE
        mov   r1, #0x0  @address  
 mov   r2, #0x30000  @size
 ;这里设置大小为192K,为什么取这个值?为什么这三个寄存器的值就作为参数传递进去了?
 ;跳转到board/smdk2410/nand_read.c中的nand_read_ll函数,代码见后面
 ;该函数需要三个参数,r0为其在ram中的起始地址,r1为在源地址也就是flash中的起始地址,r2为需要
 ;复制的中大小
        bl    nand_read_ll
 
 tst   r0, #0x0 ;r0为返回值
 ;如果成功完成复制则跳转到ok_nand_read
        beq   ok_nand_read
#ifdef CONFIG_DEBUG_LL
bad_nand_read:
        ldr   r0, STR_FAIL
 ldr   r1, SerBase
        bl    PrintWord
1: b     1b          @ infinite loop
#endif
;打印信息
ok_nand_read:
#ifdef CONFIG_DEBUG_LL
 ldr   r0, STR_OK
 ldr   r1, SerBase
 bl    PrintWord
#endif
;校验,因为从nand flash启动只需要将bootloader的前4K代码复制到stepping stone处即可
@ verify
        mov r0, #0
 ldr   r1, =UBOOT_RAM_BASE
        mov r2, #0x400     @ 4 bytes * 1024 = 4K-bytes
go_next:
 ldr   r3, [r0], #4
        ldr   r4, [r1], #4
 teq   r3, r4
        bne  notmatch
 subs r2, r2, #4
        beq  done_nand_read
 bne  go_next
notmatch:
#ifdef CONFIG_DEBUG_LL
        sub  r0, r0, #4
 ldr   r1, SerBase
        bl    PrintHexWord
 ldr   r0, STR_FAIL
        ldr   r1, SerBase
 bl    PrintWord
#endif
1: b     1b
done_nand_read:
#ifdef CONFIG_DEBUG_LL
        ldr   r0, STR_OK
 ldr   r1, SerBase
        bl    PrintWord
#endif
;恢复断点,程序继续运行
 mov pc, r10
;内存清零???
@ clear memory
@ r0: start address
@ r1: length
        mem_clear:
 mov r2, #0
        mov r3, r2
 mov r4, r2
 mov r5, r2
 mov r6, r2
        mov r7, r2
 mov r8, r2
        mov r9, r2
clear_loop:
 stmia      r0!, {r2-r9}
 subs r1, r1, #(8 * 4)
 bne  clear_loop
 mov pc, lr
#endif  @ CONFIG_S3C2410_NAND_BOOT

/*
 *************************************************************************
 *
 * CPU_init_critical registers
 *
 * setup important registers
 * setup memory timing
 *
 *************************************************************************
 */
;对临界寄存器的初始化
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
cpu_init_crit:
 /*
  * flush v4 I/D caches
  */
 ;清空指令和数据caches
 mov r0, #0
 mcr p15, 0, r0, c7, c7, 0 /* flush v3/v4 cache */
 mcr p15, 0, r0, c8, c7, 0 /* flush v4 TLB */
 /*
  * disable MMU stuff and caches
  */
 mrc p15, 0, r0, c1, c0, 0
 bic r0, r0, #0x00002300 @ clear bits 13, 9:8 (--V- --RS)
 bic r0, r0, #0x00000087 @ clear bits 7, 2:0 (B--- -CAM)
 orr r0, r0, #0x00000002 @ set bit 2 (A) Align
 orr r0, r0, #0x00001000 @ set bit 12 (I) I-Cache
 mcr p15, 0, r0, c1, c0, 0
 /*
  * before relocating, we have to setup RAM timing
  * because memory timing is board-dependend, you will
  * find a lowlevel_init.S in your board directory.
  */
 ;在重定向代码之前,必须初始化内存时序,因为重定向时需要将flash中的代码复制到内存中
 ;内存初始化的代码在开发板目录下
 mov ip, lr
 bl lowlevel_init
 mov lr, ip
 mov pc, lr
#endif /* CONFIG_SKIP_LOWLEVEL_INIT */
/*
 *************************************************************************
 *
 * Interrupt handling
 *
 *************************************************************************
 */
;中断处理
@
@ IRQ stack frame.
@
#define S_FRAME_SIZE 72
#define S_OLD_R0 68
#define S_PSR  64
#define S_PC  60
#define S_LR  56
#define S_SP  52
#define S_IP  48
#define S_FP  44
#define S_R10  40
#define S_R9  36
#define S_R8  32
#define S_R7  28
#define S_R6  24
#define S_R5  20
#define S_R4  16
#define S_R3  12
#define S_R2  8
#define S_R1  4
#define S_R0  0
#define MODE_SVC 0x13
#define I_BIT  0x80
/*
 * use bad_save_user_regs for abort/prefetch/undef/swi ...
 * use irq_save_user_regs / irq_restore_user_regs for IRQ/FIQ handling
 */
 .macro bad_save_user_regs
 sub sp, sp, #S_FRAME_SIZE
 stmia sp, {r0 - r12}   @ Calling r0-r12
 ldr r2, _armboot_start
 sub r2, r2, #(CONFIG_STACKSIZE+CFG_MALLOC_LEN)
 sub r2, r2, #(CFG_GBL_DATA_SIZE+8)  @ set base 2 words into abort stack
 ldmia r2, {r2 - r3}   @ get pc, cpsr
 add r0, sp, #S_FRAME_SIZE  @ restore sp_SVC
 add r5, sp, #S_SP
 mov r1, lr
 stmia r5, {r0 - r3}   @ save sp_SVC, lr_SVC, pc, cpsr
 mov r0, sp
 .endm
 .macro irq_save_user_regs
 sub sp, sp, #S_FRAME_SIZE
 stmia sp, {r0 - r12}   @ Calling r0-r12
 add     r8, sp, #S_PC
 stmdb   r8, {sp, lr}^                   @ Calling SP, LR
 str     lr, [r8, #0]                    @ Save calling PC
 mrs     r6, spsr
 str     r6, [r8, #4]                    @ Save CPSR
 str     r0, [r8, #8]                    @ Save OLD_R0
 mov r0, sp
 .endm
 .macro irq_restore_user_regs
 ldmia sp, {r0 - lr}^   @ Calling r0 - lr
 mov r0, r0
 ldr lr, [sp, #S_PC]   @ Get PC
 add sp, sp, #S_FRAME_SIZE
 subs pc, lr, #4   @ return & move spsr_svc into cpsr
 .endm
 .macro get_bad_stack
 ldr r13, _armboot_start  @ setup our mode stack
 sub r13, r13, #(CONFIG_STACKSIZE+CFG_MALLOC_LEN)
 sub r13, r13, #(CFG_GBL_DATA_SIZE+8) @ reserved a couple spots in abort stack
 str lr, [r13]   @ save caller lr / spsr
 mrs lr, spsr
 str     lr, [r13, #4]
 mov r13, #MODE_SVC   @ prepare SVC-Mode
 @ msr spsr_c, r13
 msr spsr, r13
 mov lr, pc
 movs pc, lr
 .endm
 .macro get_irq_stack   @ setup IRQ stack
 ldr sp, IRQ_STACK_START
 .endm
 .macro get_fiq_stack   @ setup FIQ stack
 ldr sp, FIQ_STACK_START
 .endm
/*
 * exception handlers
 */
 .align  5
undefined_instruction:
 get_bad_stack
 bad_save_user_regs
 bl  do_undefined_instruction
 .align 5
software_interrupt:
 get_bad_stack
 bad_save_user_regs
 bl  do_software_interrupt
 .align 5
prefetch_abort:
 get_bad_stack
 bad_save_user_regs
 bl  do_prefetch_abort
 .align 5
data_abort:
 get_bad_stack
 bad_save_user_regs
 bl  do_data_abort
 .align 5
not_used:
 get_bad_stack
 bad_save_user_regs
 bl  do_not_used
#ifdef CONFIG_USE_IRQ
 .align 5
irq:
 get_irq_stack
 irq_save_user_regs
 bl  do_irq
 irq_restore_user_regs
 .align 5
fiq:
 get_fiq_stack
 /* someone ought to write a more effiction fiq_save_user_regs */
 irq_save_user_regs
 bl  do_fiq
 irq_restore_user_regs
#else
 .align 5
irq:
 get_bad_stack
 bad_save_user_regs
 bl  do_irq
 .align 5
fiq:
 get_bad_stack
 bad_save_user_regs
 bl  do_fiq
#endif
#ifdef CONFIG_S3C2410_NAND_BOOT
 .align     2
 DW_STACK_START:
 .word      STACK_BASE+STACK_SIZE-4
#endif  
//该函数来自于board/smdk2410/nand_read.c
//通过r0,r1,r2三个寄存器传递参数过来,返回值为r0
int nand_read_ll(unsigned char *buf, unsigned long start_addr, int size)
{
 int i, j;
 if ((start_addr & NAND_BLOCK_MASK) || (size & NAND_BLOCK_MASK)) {
  return -1; /* invalid alignment */
 }
 //按照芯片的时序来进行读写操作
/* chip Enable */
 NFCONF &= ~0x800;
 for(i=0; i<10; i++);
 for(i=start_addr; i < (start_addr + size);) {
/* READ0 */
  NFCMD = 0;
/* Write Address */
  NFADDR = i & 0xff;
  NFADDR = (i >> 9) & 0xff;
  NFADDR = (i >> 17) & 0xff;
  NFADDR = (i >> 25) & 0xff;
  wait_idle();
  for(j=0; j < NAND_SECTOR_SIZE; j++, i++) {
   *buf = (NFDATA & 0xff);
   buf++;
  }
 }
/* chip Disable */
 NFCONF |= 0x800; /* chip disable */
 return 0;
}
board/qt2410/lowlevel_init.S
;总线宽度和等待寄存器
#define BWSCON 0x48000000
;宽度8bits、16bits、32bits
/* BWSCON */
#define DW8    (0x0)
#define DW16    (0x1)
#define DW32    (0x2)
;等待
#define WAIT    (0x1<<2)
;UBLB标识引脚信号的类型,为0则为nWBE,为1则为nBE
#define UBLB    (0x1<<3)
;定义总线类型
#define B1_BWSCON    (DW32)
#define B2_BWSCON    (DW16)
#define B3_BWSCON    (DW16 + WAIT + UBLB)
#define B4_BWSCON    (DW16)1
#define B5_BWSCON    (DW16)
#define B6_BWSCON    (DW32)
#define B7_BWSCON    (DW32)
;bank0寄存器
/* BANK0CON */
#define B0_Tacs    0x0 /*  0clk */
#define B0_Tcos    0x0 /*  0clk */
#define B0_Tacc    0x7 /* 14clk */
#define B0_Tcoh    0x0 /*  0clk */
#define B0_Tah    0x0 /*  0clk */
#define B0_Tacp    0x0
#define B0_PMC    0x0 /* normal */
;bank1寄存器
/* BANK1CON */
#define B1_Tacs    0x0 /*  0clk */
#define B1_Tcos    0x0 /*  0clk */
#define B1_Tacc    0x7 /* 14clk */
#define B1_Tcoh    0x0 /*  0clk */
#define B1_Tah    0x0 /*  0clk */
#define B1_Tacp    0x0
#define B1_PMC    0x0
;bank2寄存器
#define B2_Tacs    0x0
#define B2_Tcos    0x0
#define B2_Tacc    0x7
#define B2_Tcoh    0x0
#define B2_Tah    0x0
#define B2_Tacp    0x0
#define B2_PMC    0x0
;bank3寄存器
#define B3_Tacs    0x0 /*  0clk */
#define B3_Tcos    0x3 /*  4clk */
#define B3_Tacc    0x7 /* 14clk */
#define B3_Tcoh    0x1 /*  1clk */
#define B3_Tah    0x0 /*  0clk */
#define B3_Tacp    0x3     /*  6clk */
#define B3_PMC    0x0 /* normal */
;bank4寄存器
#define B4_Tacs    0x0 /*  0clk */
#define B4_Tcos    0x0 /*  0clk */
#define B4_Tacc    0x7 /* 14clk */
#define B4_Tcoh    0x0 /*  0clk */
#define B4_Tah    0x0 /*  0clk */
#define B4_Tacp    0x0
#define B4_PMC    0x0 /* normal */
;bank5寄存器
#define B5_Tacs    0x0 /*  0clk */
#define B5_Tcos    0x0 /*  0clk */
#define B5_Tacc    0x7 /* 14clk */
#define B5_Tcoh    0x0 /*  0clk */
#define B5_Tah    0x0 /*  0clk */
#define B5_Tacp    0x0
#define B5_PMC    0x0 /* normal */
;bank6寄存器
#define B6_MT    0x3 /* SDRAM */
#define B6_Trcd     0x1
#define B6_SCAN    0x1 /* 9bit */
;bank7寄存器
#define B7_MT    0x3 /* SDRAM */
#define B7_Trcd    0x1 /* 3clk */
#define B7_SCAN    0x1 /* 9bit */
/* REFRESH parameter */
#define REFEN    0x1 /* Refresh enable */
#define TREFMD    0x0 /* CBR(CAS before RAS)/Auto refresh */
#define Trp    0x0 /* 2clk */
#define Trc    0x3 /* 7clk */
#define Tchr    0x2 /* 3clk */
#define REFCNT    1113 /* period=15.6us, HCLK=60Mhz, (2048+1-15.6*60) */
/**************************************/
_TEXT_BASE:
 .word TEXT_BASE
.globl lowlevel_init
lowlevel_init:
 /* memory control configuration */
 /* make r0 relative the current location so that it */
 /* reads SMRDATA out of FLASH rather than memory ! */
 ldr     r0, =SMRDATA
 ldr r1, _TEXT_BASE
 sub r0, r0, r1
 ldr r1, =BWSCON /* Bus Width Status Controller */
 add     r2, r0, #13*4
 /*added by kyle*/
 lmov  r3,pc
 lldr  r4,=0x3FFF0000
 land  r3,r3,r4
 laad  r0,r0,r3
 ladd  r2,r2,r3
0:
 ldr     r3, [r0], #4
 str     r3, [r1], #4
 cmp     r2, r0
 bne     0b
 /* everything is fine now */
 mov pc, lr
 .ltorg
/* the literal pools origin */
SMRDATA:
    .word (0+(B1_BWSCON<<4)+(B2_BWSCON<<8)+(B3_BWSCON<<12)+(B4_BWSCON<<16)+(B5_BWSCON<<20)+(B6_BWSCON<<24)+(B7_BWSCON<<28))
    .word ((B0_Tacs<<13)+(B0_Tcos<<11)+(B0_Tacc<<8)+(B0_Tcoh<<6)+(B0_Tah<<4)+(B0_Tacp<<2)+(B0_PMC))
    .word ((B1_Tacs<<13)+(B1_Tcos<<11)+(B1_Tacc<<8)+(B1_Tcoh<<6)+(B1_Tah<<4)+(B1_Tacp<<2)+(B1_PMC))
    .word ((B2_Tacs<<13)+(B2_Tcos<<11)+(B2_Tacc<<8)+(B2_Tcoh<<6)+(B2_Tah<<4)+(B2_Tacp<<2)+(B2_PMC))
    .word ((B3_Tacs<<13)+(B3_Tcos<<11)+(B3_Tacc<<8)+(B3_Tcoh<<6)+(B3_Tah<<4)+(B3_Tacp<<2)+(B3_PMC))
    .word ((B4_Tacs<<13)+(B4_Tcos<<11)+(B4_Tacc<<8)+(B4_Tcoh<<6)+(B4_Tah<<4)+(B4_Tacp<<2)+(B4_PMC))
    .word ((B5_Tacs<<13)+(B5_Tcos<<11)+(B5_Tacc<<8)+(B5_Tcoh<<6)+(B5_Tah<<4)+(B5_Tacp<<2)+(B5_PMC))
    .word ((B6_MT<<15)+(B6_Trcd<<2)+(B6_SCAN))
    .word ((B7_MT<<15)+(B7_Trcd<<2)+(B7_SCAN))
    .word ((REFEN<<23)+(TREFMD<<22)+(Trp<<20)+(Trc<<18)+(Tchr<<16)+REFCNT)
    .word 0x32
    .word 0x30
    .word 0x30
;定义函数指针
typedef int (init_fnc_t) (void);
;定义函数指针数组,对硬件初始化按照该数组进行
init_fnc_t *init_sequence[] = {
 cpu_init,  //cpu/arm920t/cpu.c中定义,该函数为空,因为没有采用IRQ或FIQ模式
 board_init,  //board/smdk2410/smdk2410.c
 interrupt_init,  //cpu/arm920t/s3c24x0/interrupt.c
 env_init,  //tools/env/FW_env.c
 init_baudrate,  //lib_arm/board.c
 serial_init,  //cpu/arm920t/s3c24x0/serial.c
 console_init_f,  //common/console.c
 display_banner,  //lib_arm/board.c
#if defined(CONFIG_DISPLAY_CPUINFO)
 print_cpuinfo,  //
#endif
#if defined(CONFIG_DISPLAY_BOARDINFO)
 checkboard,  //
#endif
 dram_init,  //board/smdk2410/smdk2410.c
 display_dram_config,//lib_arm/board.c
 NULL,
};
int board_init (void)
{
 ;将时间相关的寄存器定义为结构体S3C24X0_CLOCK_POWER,S3C24X0_GPIO也是一样
 S3C24X0_CLOCK_POWER * const clk_power = S3C24X0_GetBase_CLOCK_POWER();
 S3C24X0_GPIO * const gpio = S3C24X0_GetBase_GPIO();
 ;设置cpu时钟
 /* to reduce PLL lock time, adjust the LOCKTIME register */
 clk_power->LOCKTIME = 0xFFFFFF;
 /* configure MPLL */
 //M_MDIV=0xA1,M_PDIV=0x3,M_SDIV=0x1
 //这样系统时钟为202.80M
 clk_power->MPLLCON = ((M_MDIV << 12) + (M_PDIV << 4) + M_SDIV);
 /* some delay between MPLL and UPLL */
 delay (4000);
 ;USB时钟为48M
 /* configure UPLL */
 clk_power->UPLLCON = ((U_M_MDIV << 12) + (U_M_PDIV << 4) + U_M_SDIV);
 /* some delay between MPLL and UPLL */
 delay (8000);
 ;设置GPIO
 /* set up the I/O ports */
 gpio->GPACON = 0x007FFFFF;
 gpio->GPBCON = 0x00044555;
 gpio->GPBUP = 0x000007FF;
 gpio->GPCCON = 0xAAAAAAAA;
 gpio->GPCUP = 0x0000FFFF;
 gpio->GPDCON = 0xAAAAAAAA;
 gpio->GPDUP = 0x0000FFFF;
 gpio->GPECON = 0xAAAAAAAA;
 gpio->GPEUP = 0x0000FFFF;
 gpio->GPFCON = 0x000055AA;
 gpio->GPFUP = 0x000000FF;
 gpio->GPGCON = 0xFF95FFBA;
 gpio->GPGUP = 0x0000FFFF;
 gpio->GPHCON = 0x002AFAAA;
 gpio->GPHUP = 0x000007FF;
 ;初始化bd结构体中的bi_arch_number和bi_boot_params
 /* arch number of SMDK2410-Board */
 gd->bd->bi_arch_number = MACH_TYPE_SMDK2410;
 /* adress of boot parameters */
 gd->bd->bi_boot_params = 0x30000100;
 ;启用指令和数据cache
 ;通过对协处理器的操作了实现cache的使能
 icache_enable();
 dcache_enable();
 return 0;
}
int interrupt_init (void)
{
 ;获取计时控制寄存器
 S3C24X0_TIMERS * const timers = S3C24X0_GetBase_TIMERS();
 ;使用PWM定时器4
 /* use PWM Timer 4 because it has no output */
 /* prescaler for Timer 4 is 16 */
 timers->TCFG0 = 0x0f00;
 if (timer_load_val == 0)
 {
  /*
   * for 10 ms clock period @ PCLK with 4 bit divider = 1/2
   * (default) and prescaler = 16. Should be 10390
   * @33.25MHz and 15625 @ 50 MHz
   */
  timer_load_val = get_PCLK()/(2 * 16 * 100);
 }
 /* load value for 10 ms timeout */
 lastdec = timers->TCNTB4 = timer_load_val;
 /* auto load, manual update of Timer 4 */
 timers->TCON = (timers->TCON & ~0x0700000) | 0x600000;
 /* auto load, start Timer 4 */
 timers->TCON = (timers->TCON & ~0x0700000) | 0x500000;
 timestamp = 0;
 return (0);
}
static int env_init (void)
{
 int crc1, crc1_ok;
 uchar *addr1;
 int crc2, crc2_ok;
 uchar flag1, flag2, *addr2;
 //解析参数,定义了两个envdev_t型变量
 //typedef struct envdev_s {
 //uchar devname[16];  /* Device name */
 //ulong devoff;   /* Device offset */
 //ulong env_size;   /* environment size */
 //ulong erase_size;  /* device erase size */
  //} envdev_t;
  //程序中定义了/dev/mtd1和/dev/mtd2两个,parse_config函数用来初始化这两个结构体,并利用stat函数
  //初始化一个struct stat结构体
 if (parse_config ())  /* should fill envdevices */
  return 1;
 //为参数分配空间,ENV_SIZE=CFG_ENV_SIZE - ENV_HEADER_SIZE=0x10000-sizeof(unsigned long)
 if ((addr1 = calloc (1, ENV_SIZE)) == NULL) {
  fprintf (stderr,
   "Not enough memory for environment (%ld bytes)n",
   ENV_SIZE);
  return (errno);
 }
 //从flash中读取参数
 //typedef struct environment_s {
 //ulong crc;   /* CRC32 over data bytes    */
 //uchar flags;   /* active or obsolete */
 //uchar *data;
 //} env_t;
 /* read environment from FLASH to local buffer */
 //确定其指针
 environment.data = addr1;
 curdev = 0;
 ;该函数用来将旧的参数从flash中擦除,然后将新的参数写入flash
 if (flash_io (O_RDONLY)) {
  return (errno);
 }
 ;进行crc校验
 crc1_ok = ((crc1 = crc32 (0, environment.data, ENV_SIZE))
      == environment.crc);
 if (!HaveRedundEnv) {
  if (!crc1_ok) {
   fprintf (stderr,
    "Warning: Bad CRC, using default environmentn");
   memcpy(environment.data, default_environment, sizeof default_environment);
  }
 } else {
  flag1 = environment.flags;
  curdev = 1;
  if ((addr2 = calloc (1, ENV_SIZE)) == NULL) {
   fprintf (stderr,
    "Not enough memory for environment (%ld bytes)n",
    ENV_SIZE);
   return (errno);
  }
  environment.data = addr2;
  if (flash_io (O_RDONLY)) {
   return (errno);
  }
  crc2_ok = ((crc2 = crc32 (0, environment.data, ENV_SIZE))
       == environment.crc);
  flag2 = environment.flags;
  if (crc1_ok && !crc2_ok) {
   environment.data = addr1;
   environment.flags = flag1;
   environment.crc = crc1;
   curdev = 0;
   free (addr2);
  } else if (!crc1_ok && crc2_ok) {
   environment.data = addr2;
   environment.flags = flag2;
   environment.crc = crc2;
   curdev = 1;
   free (addr1);
  } else if (!crc1_ok && !crc2_ok) {
   fprintf (stderr,
    "Warning: Bad CRC, using default environmentn");
   memcpy(environment.data, default_environment, sizeof default_environment);
   curdev = 0;
   free (addr1);
  } else if (flag1 == active_flag && flag2 == obsolete_flag) {
   environment.data = addr1;
   environment.flags = flag1;
   environment.crc = crc1;
   curdev = 0;
   free (addr2);
  } else if (flag1 == obsolete_flag && flag2 == active_flag) {
   environment.data = addr2;
   environment.flags = flag2;
   environment.crc = crc2;
   curdev = 1;
   free (addr1);
  } else if (flag1 == flag2) {
   environment.data = addr1;
   environment.flags = flag1;
   environment.crc = crc1;
   curdev = 0;
   free (addr2);
  } else if (flag1 == 0xFF) {
   environment.data = addr1;
   environment.flags = flag1;
   environment.crc = crc1;
   curdev = 0;
   free (addr2);
  } else if (flag2 == 0xFF) {
   environment.data = addr2;
   environment.flags = flag2;
   environment.crc = crc2;
   curdev = 1;
   free (addr1);
  }
 }
 return (0);
}
//如果参数中设置了波特率则利用参数用设置的波特率,否则利用默认的CONFIG_BAUDRATE(115200)
static int init_baudrate (void)
{
 char tmp[64]; /* long enough for environment variables */
 int i = getenv_r ("baudrate", tmp, sizeof (tmp));
 gd->bd->bi_baudrate = gd->baudrate = (i > 0)
   ? (int) simple_strtoul (tmp, NULL, 10)
   : CONFIG_BAUDRATE;
 return (0);
}
int serial_init (void)
{
 //该函数设置uart的几个寄存器,包括FIFO寄存器、控制寄存器、列控制寄存器和波特率约数寄存器等
 serial_setbrg ();
 return (0);
}
;设置gd->have_console
int console_init_f (void)
{
 gd->have_console = 1;
#ifdef CONFIG_SILENT_CONSOLE
 if (getenv("silent") != NULL)
  gd->flags |= GD_FLG_SILENT;
#endif
 return (0);
}
//打印当前一些状态信息
static int display_banner (void)
{
 printf ("nn%snn", version_string);
 debug ("U-Boot code: %08lX -> %08lX  BSS: -> %08lXn",
        _armboot_start, _bss_start, _bss_end);
#ifdef CONFIG_MODEM_SUPPORT
 debug ("Modem Support enabledn");
#endif
#ifdef CONFIG_USE_IRQ
 debug ("IRQ Stack: %08lxn", IRQ_STACK_START);
 debug ("FIQ Stack: %08lxn", FIQ_STACK_START);
#endif
 return (0);
}
//初始化ram信息,设置起始地址和大小,从include/configs/smdk2410.h中获取这些信息
int dram_init (void)
{
 gd->bd->bi_dram[0].start = PHYS_SDRAM_1;
 gd->bd->bi_dram[0].size = PHYS_SDRAM_1_SIZE;
 return 0;
}
//显示ram信息,其中的宏也是从include/configs/smdk2410.h中读取
static int display_dram_config (void)
{
 int i;
#ifdef DEBUG
 puts ("RAM Configuration:n");
 for(i=0; i  printf ("Bank #%d: %08lx ", i, gd->bd->bi_dram[i].start);
  print_size (gd->bd->bi_dram[i].size, "n");
 }
#else
 ulong size = 0;
 for (i=0; i  size += gd->bd->bi_dram[i].size;
 }
 puts("DRAM:  ");
 print_size(size, "n");
#endif
 return (0);
}
//以上都是一些初始化的函数,可以看出以上这些函数都是为了初始化一个全局的结构体变量gd而执行的,
//该变量地址由寄存器r8指向,该结构体定义了开发板的相关硬件配置,在include/asm-arm/global_data.h中
//定义
/*
typedef struct global_data {
 bd_t  *bd;//开发板参数
 unsigned long flags;
 unsigned long baudrate;
 unsigned long have_console; /* serial_init() was called */
 unsigned long reloc_off; /* Relocation Offset */
 unsigned long env_addr; /* Address  of Environment struct */
 unsigned long env_valid; /* Checksum of Environment valid? */
 unsigned long fb_base; /* base address of frame buffer */
#ifdef CONFIG_VFD
 unsigned char vfd_type; /* display type */
#endif
#if 0
 unsigned long cpu_clk; /* CPU clock in Hz!  */
 unsigned long bus_clk;
 unsigned long ram_size; /* RAM size */
 unsigned long reset_status; /* reset status register at boot */
#endif
 void  **jt;  /* jump table */
} gd_t;
*/
void start_armboot (void)
{
 init_fnc_t **init_fnc_ptr;
 char *s;
#ifndef CFG_NO_FLASH
 ulong size;
#endif
#if defined(CONFIG_VFD) || defined(CONFIG_LCD)
 unsigned long addr;
#endif
 /* Pointer is writable since we allocated a register for it */
 //获取全局gd指针
 gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t));
 /* compiler optimization barrier needed for GCC >= 3.4 */
 __asm__ __volatile__("": : :"memory");
 //清空该结构体
 memset ((void*)gd, 0, sizeof (gd_t));
 //获取bd_info结构体指针
 gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));
 memset (gd->bd, 0, sizeof (bd_t));
 //整个代码区的长度
 monitor_flash_len = _bss_start - _armboot_start;
 //调用初始化函数,用来初始化gd结构体
 for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
  if ((*init_fnc_ptr)() != 0) {
   hang ();
  }
 }
#ifndef CFG_NO_FLASH
 /* configure available FLASH banks */
 //board/smdk2410/flash.c配置flash
 //从其实现来看,好像只是配置nor flash
 size = flash_init ();
 //显示flash信息
 display_flash_config (size);
#endif /* CFG_NO_FLASH */
//定义显示类型
#ifdef CONFIG_VFD
# ifndef PAGE_SIZE
#   define PAGE_SIZE 4096
# endif
 /*
  * reserve memory for VFD display (always full pages)
  */
 /* bss_end is defined in the board-specific linker script */
 //按页对其方式保留显存
 addr = (_bss_end + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
 size = vfd_setmem (addr);
 gd->fb_base = addr;
#endif /* CONFIG_VFD */
//显示器为LCD,同上
#ifdef CONFIG_LCD
# ifndef PAGE_SIZE
#   define PAGE_SIZE 4096
# endif
 /*
  * reserve memory for LCD display (always full pages)
  */
 /* bss_end is defined in the board-specific linker script */
 addr = (_bss_end + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
 size = lcd_setmem (addr);
 gd->fb_base = addr;
#endif /* CONFIG_LCD */
//初始化CFG_MALLOC_LEN大小空间
 /* armboot_start is defined in the board-specific linker script */
 mem_malloc_init (_armboot_start - CFG_MALLOC_LEN);
//初始化nand flash,这是在nand flash启动的s3c2410移植u-boot的关键,根据flash时序编写函数即可
//在include/configs/smdk2410.h中的command definition中增加CONFIG_COMMANDS和CFG_CMD_NAND命令
#if (CONFIG_COMMANDS & CFG_CMD_NAND)
 puts ("NAND:  ");
 nand_init();  //board/smdk2410/smdk2410.c
#endif
#ifdef CONFIG_HAS_DATAFLASH
 AT91F_DataflashInit();
 dataflash_print_info();
#endif
 /* initialize environment */
 //初始化环境参数
 env_relocate ();
 //framebuffer初始化
#ifdef CONFIG_VFD
 /* must do this after the framebuffer is allocated */
 drv_vfd_init();
#endif /* CONFIG_VFD */
//通过命令行参数传递获取ip地址
 /* IP Address */
 gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr");
//通过命令行参数传递获取物理地址
 /* MAC Address */
 {
  int i;
  ulong reg;
  char *s, *e;
  char tmp[64];
  i = getenv_r ("ethaddr", tmp, sizeof (tmp));
  s = (i > 0) ? tmp : NULL;
  for (reg = 0; reg < 6; ++reg) {
   gd->bd->bi_enetaddr[reg] = s ? simple_strtoul (s, &e, 16) : 0;
   if (s)
    s = (*e) ? e + 1 : e;
  }
#ifdef CONFIG_HAS_ETH1
  i = getenv_r ("eth1addr", tmp, sizeof (tmp));
  s = (i > 0) ? tmp : NULL;
  for (reg = 0; reg < 6; ++reg) {
   gd->bd->bi_enet1addr[reg] = s ? simple_strtoul (s, &e, 16) : 0;
   if (s)
    s = (*e) ? e + 1 : e;
  }
#endif
 }
 //调用相应驱动函数对硬件设备进行初始化
 devices_init (); /* get the devices list going. */
#ifdef CONFIG_CMC_PU2
 load_sernum_ethaddr ();
#endif /* CONFIG_CMC_PU2 */
 jumptable_init ();
 //初始化串口
 console_init_r (); /* fully init console as a device */
#if defined(CONFIG_MISC_INIT_R)
 /* miscellaneous platform dependent initialisations */
 misc_init_r ();
#endif
 /* enable exceptions */
 //启用中断
 enable_interrupts ();
 /* Perform network card initialisation if necessary */
 //初始化网卡
#ifdef CONFIG_DRIVER_CS8900
 cs8900_get_enetaddr (gd->bd->bi_enetaddr);
#endif
#if defined(CONFIG_DRIVER_SMC91111) || defined (CONFIG_DRIVER_LAN91C96)
 if (getenv ("ethaddr")) {
  smc_set_mac_addr(gd->bd->bi_enetaddr);
 }
#endif /* CONFIG_DRIVER_SMC91111 || CONFIG_DRIVER_LAN91C96 */
 /* Initialize from environment */
 if ((s = getenv ("loadaddr")) != NULL) {
  load_addr = simple_strtoul (s, NULL, 16);
 }
#if (CONFIG_COMMANDS & CFG_CMD_NET)
 if ((s = getenv ("bootfile")) != NULL) {
  copy_filename (BootFile, s, sizeof (BootFile));
 }
#endif /* CFG_CMD_NET */
#ifdef BOARD_LATE_INIT
 board_late_init ();
#endif
#if (CONFIG_COMMANDS & CFG_CMD_NET)
#if defined(CONFIG_NET_MULTI)
 puts ("Net:   ");
#endif
 eth_initialize(gd->bd);
#endif
 /* main_loop() can return to retry autoboot, if so just run it again. */
 for (;;) {
  main_loop ();
 }
 /* NOTREACHED - no way out of command loop except booting */
}
//在board/smdk2410/smdk2410.c中增加nand flash初始化代码
/*
   * NAND flash initialization.
   */
//在include/s3c24x0.h中由S3C2410_NAND结构体的定义,也就是定义了NFCONF、NFCMD、NFADDR和NFSTAT这四个//寄存器,由于2410有flash控制器,因此可以直接通过flash控制器来对flash器件进行操作,如果芯片没有//flash控制器,有的利用I/O端口来同flash交换数据,就必须利用I/O口来模式时序。比如44B0X就是利用GPIO
//来进行flash读写的
#if (CONFIG_COMMANDS & CFG_CMD_NAND)
typedef enum {
 NFCE_LOW,
 NFCE_HIGH,
} NFCE_STATE;
extern unsigned long nand_probe(unsigned long physadr);
static inline void NF_Reset(void)
{
 int i;
 //使能
 NF_SetCE(NFCE_LOW);
 //写入命令
 NF_Cmd(0xFF); // reset command
 //延时
 for(i = 0; i < 10; i++); // tWB = 100ns.
 //通过R/B信号线,判断flash内部操作是否完成,
 NF_WaitRB(); // wait 200~500us;
 //芯片禁用
 NF_SetCE(NFCE_HIGH);
}
static inline void NF_Init(void)
{
#if 0 // a little bit too optimistic
#define TACLS 0
#define TWRPH0 3
#define TWRPH1 0
#else
#define TACLS 0
#define TWRPH0 4
#define TWRPH1 2
#endif
 //首先根据NFCONF寄存器的各位定义对Nand flash进行初始化
 NF_Conf((1<<15)|(0<<14)|(0<<13)|(1<<12)|(1<<11)|(TACLS<<8)|(TWRPH0<<4)|(TWRPH1<<0));
 NF_Reset();
}
void nand_init(void)
{
 S3C2410_NAND * const nand = S3C2410_GetBase_NAND();
 NF_Init();
 //利用drivers/nand_legacy/nand_legacy.c,在该文件中也添加这几个同nand flash相关的函数
 printf ("%4lu MBn", nand_probe((ulong)nand) >> 20);
}
//操作控制寄存器,该寄存器为16位寄存器
static  void NF_Conf(u16 conf)
{
 S3C2410_NAND * const nand = S3C2410_GetBase_NAND();
 nand->NFCONF = conf;
}
//flash只有8位的输入、输出端口,直接向命令寄存器赋值即可写入命令
static  void NF_Cmd(u8 cmd)
{
 S3C2410_NAND * const nand = S3C2410_GetBase_NAND();
 nand->NFCMD = cmd;
}
static void NF_CmdW(u8 cmd)
{
 NF_Cmd(cmd);
 udelay(1);
}
//写入地址
static  void NF_Addr(u8 addr)
{
 S3C2410_NAND * const nand = S3C2410_GetBase_NAND();
 nand->NFADDR = addr;
}
//芯片使能
static  void NF_SetCE(NFCE_STATE s)
{
 S3C2410_NAND * const nand = S3C2410_GetBase_NAND();
 //Nand flash memroy chip enable,如果NFCONF的第11位为1则nFCE使能,为1则禁止
 switch (s) {
  case NFCE_LOW:
   nand->NFCONF &= ~(1<<11);
   break;
  case NFCE_HIGH:
   nand->NFCONF |= (1<<11);
   break;
 }
}
//等待flash内部操作完成
static void NF_WaitRB(void)
{
 S3C2410_NAND * const nand = S3C2410_GetBase_NAND();
 while (!(nand->NFSTAT & 0x01));
}
//向数据寄存器写入数据
static  void NF_Write(u8 data)
{
 S3C2410_NAND * const nand = S3C2410_GetBase_NAND();
 nand->NFDATA = data;
}
//读取数据
static  u8 NF_Read(void)
{
 S3C2410_NAND * const nand = S3C2410_GetBase_NAND();
 return(nand->NFDATA);
}
//校验
static  void NF_Init_ECC(void)
{
 S3C2410_NAND * const nand = S3C2410_GetBase_NAND();
 nand->NFCONF |= (1<<12);
}
//获取ECC code
static  u32 NF_Read_ECC(void)
{
 S3C2410_NAND * const nand = S3C2410_GetBase_NAND();
 return(nand->NFECC);
}
#endif
通过上面的描述要移植U-Boot到s3c2410上,并要从nand flash启动,则需要修改的地方有:
1、在start.S中增加将U-Boot从flash复制到ram中的代码
2、在include/configs/smdk2410.h中增加相关定义,主要是nand flash寄存器的定义,并针对具体平台对其中的参数进行配置,比如ram的大小等。
3、在board/smdk2410/smdk2410.c中增加nand flash的相关操作函数,如复位、写指令、读数据、查看状态等。
再编译U-Boot则可以看到flash的定义,并能通过网络下载内核和文件系统等。
当U-Boot进入main_loop后将等待用户输入,或者在定时时间超过时调用do_bootm_linux来启动linux内核,
U-Boot为各个处理器的do_bootm_linux定义了固定的类型,定义了boot_os_Fcn函数类型。
typedef void boot_os_Fcn (cmd_tbl_t *cmdtp, int flag,
     int argc, char *argv[],
     ulong addr,  /* of image to boot */
     ulong *len_ptr, /* multi-file image length table */
     int verify); /* getenv("verify")[0] != 'n' */
cmdtp结构体用来保存命令以及参数,addr为启动镜像的地址,程序首先进入do_bootm函数
int do_bootm (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
 ulong iflag;
 ulong addr;
 ulong data, len, checksum;
 ulong  *len_ptr;
 //定义最大的解压空间为8M
 uint unc_len = CFG_BOOTM_LEN;
 int i, verify;
 char *name, *s;
 int (*appl)(int, char *[]);
 //镜像文件的头部信息,传递给do_bootm_linux函数
 image_header_t *hdr = &header;
 s = getenv ("verify");
 verify = (s && (*s == 'n')) ? 0 : 1;
 //如果没有指定运行地址,则采用默认的地址,该地址在include/configs/smdk2410.h中定义为0x30008000
 if (argc < 2) {
  addr = load_addr;
 } else {
 //如果参数中指定了地址,则从指定地址开始运行
  addr = simple_strtoul(argv[1], NULL, 16);
 }
 //在文件开头定义CONFIG_SHOW_BOOT_PROGRESS函数用来显示当前内核运行到哪一步了
 SHOW_BOOT_PROGRESS (1);
 printf ("## Booting image at %08lx ...n", addr);
 //系统中没有atmel的数据flash
 /* Copy header so we can blank CRC field for re-calculation */
#ifdef CONFIG_HAS_DATAFLASH
 if (addr_dataflash(addr)){
  read_dataflash(addr, sizeof(image_header_t), (char *)&header);
 } else
#endif
 //从镜像读取头部到结构header中
 memmove (&header, (char *)addr, sizeof(image_header_t));
 //通过头部结构体来获取magic、crc等数据
 if (ntohl(hdr->ih_magic) != IH_MAGIC) {
 //i386
#ifdef __I386__ /* correct image format not implemented yet - fake it */
 //通过头部结构体,检测镜像的格式是否正确
  if (fake_header(hdr, (void*)addr, -1) != NULL) {
   /* to compensate for the addition below */
   addr -= sizeof(image_header_t);
   /* turnof verify,
    * fake_header() does not fake the data crc
    */
   verify = 0;
  } else
#endif /* __I386__ */
 //对于非i386平台
     {
  puts ("Bad Magic Numbern");
  SHOW_BOOT_PROGRESS (-1);
  return 1;
     }
 }
 //magic正确为第2步
 SHOW_BOOT_PROGRESS (2);
 //校验crc
 data = (ulong)&header;
 len  = sizeof(image_header_t);
 checksum = ntohl(hdr->ih_hcrc);
 hdr->ih_hcrc = 0;
 if (crc32 (0, (uchar *)data, len) != checksum) {
  puts ("Bad Header Checksumn");
  SHOW_BOOT_PROGRESS (-2);
  return 1;
 }
 //校验成功为第3步
 SHOW_BOOT_PROGRESS (3);
#ifdef CONFIG_HAS_DATAFLASH
 if (addr_dataflash(addr)){
  len  = ntohl(hdr->ih_size) + sizeof(image_header_t);
  read_dataflash(addr, len, (char *)CFG_LOAD_ADDR);
  addr = CFG_LOAD_ADDR;
 }
#endif
 //对于多文件镜像的处理,这个部分是很有用的,特别对于容量很小的系统,有时候需要将代码段和数据段放//在不同的文件中
 
 //显示镜像文件的信息,包括文件大小、创建时间、入口等
 print_image_hdr ((image_header_t *)addr);
 //data为镜像文件头部后的第1个ulong数据,用来完成crc校验
 data = addr + sizeof(image_header_t);
 //len为镜像问价的大小
 len  = ntohl(hdr->ih_size);
 //是否进行crc校验
 if (verify) {
  puts ("   Verifying Checksum ... ");
  if (crc32 (0, (uchar *)data, len) != ntohl(hdr->ih_dcrc)) {
   printf ("Bad Data CRCn");
   SHOW_BOOT_PROGRESS (-3);
   return 1;
  }
  puts ("OKn");
 }
 //校验成功为第4步
 SHOW_BOOT_PROGRESS (4);
 //数据指针
 len_ptr = (ulong *)data;
 //校验cpu的体系结构
#if defined(__PPC__)
 if (hdr->ih_arch != IH_CPU_PPC)
#elif defined(__ARM__)
 if (hdr->ih_arch != IH_CPU_ARM)
#elif defined(__I386__)
 if (hdr->ih_arch != IH_CPU_I386)
#elif defined(__mips__)
 if (hdr->ih_arch != IH_CPU_MIPS)
#elif defined(__nios__)
 if (hdr->ih_arch != IH_CPU_NIOS)
#elif defined(__M68K__)
 if (hdr->ih_arch != IH_CPU_M68K)
#elif defined(__microblaze__)
 if (hdr->ih_arch != IH_CPU_MICROBLAZE)
#elif defined(__nios2__)
 if (hdr->ih_arch != IH_CPU_NIOS2)
#elif defined(__blackfin__)
 if (hdr->ih_arch != IH_CPU_BLACKFIN)
#else
# error Unknown CPU type
#endif
 {
  printf ("Unsupported Architecture 0x%xn", hdr->ih_arch);
  SHOW_BOOT_PROGRESS (-4);
  return 1;
 }
 //体系结构校验成功为第5步
 SHOW_BOOT_PROGRESS (5);
 //判断镜像文件类型
 switch (hdr->ih_type) {
 //如果是独立的应用程序则利用第2个参数来作为加载地址
 case IH_TYPE_STANDALONE:
  name = "Standalone Application";
  /* A second argument overwrites the load address */
  if (argc > 2) {
   hdr->ih_load = htonl(simple_strtoul(argv[2], NULL, 16));
  }
  break;
  //单文件类型内核
 case IH_TYPE_KERNEL:
  name = "Kernel Image";
  break;
  //多文件类型内核
 case IH_TYPE_MULTI:
  name = "Multi-File Image";
  //获取内核镜像大小
  len  = ntohl(len_ptr[0]);
  /* OS kernel is always the first image */
  data += 8; /* kernel_len + terminator */
  for (i=1; len_ptr[i]; ++i)
   data += 4;
  break;
 default: printf ("Wrong Image Type for %s commandn", cmdtp->name);
  SHOW_BOOT_PROGRESS (-5);
  return 1;
 }
 //类型判断成功为第6步
 SHOW_BOOT_PROGRESS (6);
 /*
  * We have reached the point of no return: we are going to
  * overwrite all exception vector code, so we cannot easily
  * recover from any failures any more...
  */
 //关闭所有中断
 iflag = disable_interrupts();
 //处理cache
#ifdef CONFIG_AMIGAONEG3SE
 /*
  * We've possible left the caches enabled during
  * bios emulation, so turn them off again
  */
 icache_disable();
 invalidate_l1_instruction_cache();
 flush_data_cache();
 dcache_disable();
#endif
//判断镜像压缩类型,并利用对应程序对内核压缩镜像进行解压
 switch (hdr->ih_comp) {
 case IH_COMP_NONE:
  if(ntohl(hdr->ih_load) == addr) {
   printf ("   XIP %s ... ", name);
  } else {
#if defined(CONFIG_HW_WATCHDOG) || defined(CONFIG_WATCHDOG)
   size_t l = len;
   void *to = (void *)ntohl(hdr->ih_load);
   void *from = (void *)data;
   printf ("   Loading %s ... ", name);
   while (l > 0) {
    size_t tail = (l > CHUNKSZ) ? CHUNKSZ : l;
    WATCHDOG_RESET();
    memmove (to, from, tail);
    to += tail;
    from += tail;
    l -= tail;
   }
#else /* !(CONFIG_HW_WATCHDOG || CONFIG_WATCHDOG) */
   memmove ((void *) ntohl(hdr->ih_load), (uchar *)data, len);
#endif /* CONFIG_HW_WATCHDOG || CONFIG_WATCHDOG */
  }
  break;
 case IH_COMP_GZIP:
  printf ("   Uncompressing %s ... ", name);
  if (gunzip ((void *)ntohl(hdr->ih_load), unc_len,
       (uchar *)data, &len) != 0) {
   puts ("GUNZIP ERROR - must RESET board to recovern");
   SHOW_BOOT_PROGRESS (-6);
   do_reset (cmdtp, flag, argc, argv);
  }
  break;
#ifdef CONFIG_BZIP2
 case IH_COMP_BZIP2:
  printf ("   Uncompressing %s ... ", name);
  /*
   * If we've got less than 4 MB of malloc() space,
   * use slower decompression algorithm which requires
   * at most 2300 KB of memory.
   */
  i = BZ2_bzBuffToBuffDecompress ((char*)ntohl(hdr->ih_load),
      &unc_len, (char *)data, len,
      CFG_MALLOC_LEN < (4096 * 1024), 0);
  if (i != BZ_OK) {
   printf ("BUNZIP2 ERROR %d - must RESET board to recovern", i);
   SHOW_BOOT_PROGRESS (-6);
   udelay(100000);
   do_reset (cmdtp, flag, argc, argv);
  }
  break;
#endif /* CONFIG_BZIP2 */
 default:
 //重新打开中断
  if (iflag)
   enable_interrupts();
  //不能识别的压缩类型
  printf ("Unimplemented compression type %dn", hdr->ih_comp);
  SHOW_BOOT_PROGRESS (-7);
  return 1;
 }
 puts ("OKn");
 //解压成功为第7步
 SHOW_BOOT_PROGRESS (7);
 //如果是独立的应用程序,则根据autostart来确定是否自动启动程序
 switch (hdr->ih_type) {
 case IH_TYPE_STANDALONE:
  if (iflag)
   enable_interrupts();
  /* load (and uncompress), but don't start if "autostart"
   * is set to "no"
   */
  if (((s = getenv("autostart")) != NULL) && (strcmp(s,"no") == 0)) {
   char buf[32];
   sprintf(buf, "%lX", len);
   setenv("filesize", buf);
   return 0;
  }
  //hdr->ih_ep为程序的入口
  appl = (int (*)(int, char *[]))ntohl(hdr->ih_ep);
  //启动程序
  (*appl)(argc-1, &argv[1]);
  return 0;
 case IH_TYPE_KERNEL:
 case IH_TYPE_MULTI:
  /* handled below */
  break;
 default:
  if (iflag)
   enable_interrupts();
  printf ("Can't boot image type %dn", hdr->ih_type);
  SHOW_BOOT_PROGRESS (-8);
  return 1;
 }
 //成功为第8步
 SHOW_BOOT_PROGRESS (8);
 //判断操作系统类型
 switch (hdr->ih_os) {
 default:   /* handled by (original) Linux case */
 case IH_OS_LINUX:
#ifdef CONFIG_SILENT_CONSOLE
     fixup_silent_linux();
#endif
  printf("boot linuxn");
  //进入armlinux.c中的do_bootm_linux函数
     do_bootm_linux  (cmdtp, flag, argc, argv,
        addr, len_ptr, verify);
     break;
 case IH_OS_NETBSD:
     do_bootm_netbsd (cmdtp, flag, argc, argv,
        addr, len_ptr, verify);
     break;
#ifdef CONFIG_LYNXKDI
 case IH_OS_LYNXOS:
     do_bootm_lynxkdi (cmdtp, flag, argc, argv,
        addr, len_ptr, verify);
     break;
#endif
 case IH_OS_RTEMS:
     do_bootm_rtems (cmdtp, flag, argc, argv,
        addr, len_ptr, verify);
     break;
#if (CONFIG_COMMANDS & CFG_CMD_ELF)
 case IH_OS_VXWORKS:
     do_bootm_vxworks (cmdtp, flag, argc, argv,
         addr, len_ptr, verify);
     break;
 case IH_OS_QNX:
     do_bootm_qnxelf (cmdtp, flag, argc, argv,
         addr, len_ptr, verify);
     break;
#endif /* CFG_CMD_ELF */
#ifdef CONFIG_ARTOS
 case IH_OS_ARTOS:
     do_bootm_artos  (cmdtp, flag, argc, argv,
        addr, len_ptr, verify);
     break;
#endif
 }
 SHOW_BOOT_PROGRESS (-9);
#ifdef DEBUG
 puts ("n## Control returned to monitor - resetting...n");
 do_reset (cmdtp, flag, argc, argv);
#endif
 return 1;
}
进入文件lib_arm/arm_linux.c中的do_bootm_linux
void do_bootm_linux (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[],
       ulong addr, ulong *len_ptr, int verify)
{
 ulong len = 0, checksum;
 ulong initrd_start, initrd_end;
 ulong data;
 //内核调用入口
 void (*theKernel)(int zero, int arch, uint params);
 //获取镜像的头部信息,header在cmd_bootm.c中定义
 image_header_t *hdr = &header;
 //获取开发板信息
 bd_t *bd = gd->bd;
#ifdef CONFIG_CMDLINE_TAG
//获取bootargs参数
 char *commandline = getenv ("bootargs");
#endif
 //调用内核入口函数,将内核入口函数指针赋给theKernel
 theKernel = (void (*)(int, int, uint))ntohl(hdr->ih_ep);
 /*
  * Check if there is an initrd image
  */
  //看是否为initrd镜像
 if (argc >= 3) {
  //如果为initrd为第9步
  SHOW_BOOT_PROGRESS (9);
  //获取initrd镜像地址
  addr = simple_strtoul (argv[2], NULL, 16);
  printf ("## Loading Ramdisk Image at %08lx ...n", addr);
  
  /* Copy header so we can blank CRC field for re-calculation */
#ifdef CONFIG_HAS_DATAFLASH
  if (addr_dataflash (addr)) {
   read_dataflash (addr, sizeof (image_header_t),
     (char *) &header);
  } else
#endif
   //获取initrd头部西你想
   memcpy (&header, (char *) addr,
    sizeof (image_header_t));
  //校验magic
  if (ntohl (hdr->ih_magic) != IH_MAGIC) {
   printf ("Bad Magic Numbern");
   SHOW_BOOT_PROGRESS (-10);
   do_reset (cmdtp, flag, argc, argv);
  }
  //校验crc
  data = (ulong) & header;
  len = sizeof (image_header_t);
  checksum = ntohl (hdr->ih_hcrc);
  hdr->ih_hcrc = 0;
  if (crc32 (0, (unsigned char *) data, len) != checksum) {
   printf ("Bad Header Checksumn");
   SHOW_BOOT_PROGRESS (-11);
   do_reset (cmdtp, flag, argc, argv);
  }
  //校验成功为第10步
  SHOW_BOOT_PROGRESS (10);
  print_image_hdr (hdr);
  data = addr + sizeof (image_header_t);
  len = ntohl (hdr->ih_size);
#ifdef CONFIG_HAS_DATAFLASH
  if (addr_dataflash (addr)) {
   read_dataflash (data, len, (char *) CFG_LOAD_ADDR);
   data = CFG_LOAD_ADDR;
  }
#endif
  //checksum
  if (verify) {
   ulong csum = 0;
   printf ("   Verifying Checksum ... ");
   csum = crc32 (0, (unsigned char *) data, len);
   if (csum != ntohl (hdr->ih_dcrc)) {
    printf ("Bad Data CRCn");
    SHOW_BOOT_PROGRESS (-12);
    do_reset (cmdtp, flag, argc, argv);
   }
   printf ("OKn");
  }
  //成功为第11步
  SHOW_BOOT_PROGRESS (11);
  //判断操作系统类型
  if ((hdr->ih_os != IH_OS_LINUX) ||
      (hdr->ih_arch != IH_CPU_ARM) ||
      (hdr->ih_type != IH_TYPE_RAMDISK)) {
   printf ("No Linux ARM Ramdisk Imagen");
   SHOW_BOOT_PROGRESS (-13);
   do_reset (cmdtp, flag, argc, argv);
  }
#if defined(CONFIG_B2) || defined(CONFIG_EVB4510) || defined(CONFIG_ARMADILLO)
  /*
   *we need to copy the ramdisk to SRAM to let Linux boot
   */
  memmove ((void *) ntohl(hdr->ih_load), (uchar *)data, len);
  data = ntohl(hdr->ih_load);
#endif /* CONFIG_B2 || CONFIG_EVB4510 */
  /*
   * Now check if we have a multifile image
   */
 }
  //判断是否为多文件内核镜像
  else if ((hdr->ih_type == IH_TYPE_MULTI) && (len_ptr[1])) {
  ulong tail = ntohl (len_ptr[0]) % 4;
  int i;
  SHOW_BOOT_PROGRESS (13);
  /* skip kernel length and terminator */
  data = (ulong) (&len_ptr[2]);
  /* skip any additional image length fields */
  for (i = 1; len_ptr[i]; ++i)
   data += 4;
  /* add kernel length, and align */
  data += ntohl (len_ptr[0]);
  if (tail) {
   data += 4 - tail;
  }
  len = ntohl (len_ptr[1]);
 } else {
  /*
   * no initrd image
   */
   //无initrd镜像
  SHOW_BOOT_PROGRESS (14);
  len = data = 0;
 }
#ifdef DEBUG
 if (!data) {
  printf ("No initrdn");
 }
#endif
 //确定initrd的起始地址和终止地址
 if (data) {
  initrd_start = data;
  initrd_end = initrd_start + len;
 } else {
  initrd_start = 0;
  initrd_end = 0;
 }
 //成功为第15步
 SHOW_BOOT_PROGRESS (15);
 //进入linux内核
 debug ("## Transferring control to Linux (at address %08lx) ...n",
        (ulong) theKernel);
#if defined (CONFIG_SETUP_MEMORY_TAGS) ||
    defined (CONFIG_CMDLINE_TAG) ||
    defined (CONFIG_INITRD_TAG) ||
    defined (CONFIG_SERIAL_TAG) ||
    defined (CONFIG_REVISION_TAG) ||
    defined (CONFIG_LCD) ||
    defined (CONFIG_VFD)
    //开始设置启动参数,该函数也在本文件中定义,见下面
 setup_start_tag (bd);
#ifdef CONFIG_SERIAL_TAG
 //设置串口参数
 setup_serial_tag (¶ms);
#endif
#ifdef CONFIG_REVISION_TAG
 //设置修订参数
 setup_revision_tag (¶ms);
#endif
#ifdef CONFIG_SETUP_MEMORY_TAGS
 //设置内存参数
 setup_memory_tags (bd);
#endif
#ifdef CONFIG_CMDLINE_TAG
 //设置命令行参数
 setup_commandline_tag (bd, commandline);
#endif
#ifdef CONFIG_INITRD_TAG
 //设置initrd参数
 if (initrd_start && initrd_end)
  setup_initrd_tag (bd, initrd_start, initrd_end);
#endif
#if defined (CONFIG_VFD) || defined (CONFIG_LCD)
 //设置vfd、lcd参数
 setup_videolfb_tag ((gd_t *) gd);
#endif
 //结束参数设置
 setup_end_tag (bd);
#endif
 //启动内核
 /* we assume that the kernel is in place */
 printf ("nStarting kernel ...nn");
#ifdef CONFIG_USB_DEVICE
 {
  extern void udc_disconnect (void);
  udc_disconnect ();
 }
#endif
 //在内核启动前的清除工作
 cleanup_before_linux ();
 //调用内核
 theKernel (0, bd->bi_arch_number, bd->bi_boot_params);
}
//开始设置启动参数
static void setup_start_tag (bd_t *bd)
{
 params = (struct tag *) bd->bi_boot_params;
 params->hdr.tag = ATAG_CORE;
 params->hdr.size = tag_size (tag_core);
 params->u.core.flags = 0;
 params->u.core.pagesize = 0;
 params->u.core.rootdev = 0;
 params = tag_next (params);
}
阅读(870) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~