第一章 Uboot1.1.16中的汇编部分
老版本的Uboot1.1.16是从start.S (uboot1.1.16_256m-for36---v1.01\cpu\s3c64xx)开始执行的。
1.中断向量表和中断地址表。
.globl _start
_start: b reset
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
_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
_pad:
.word 0x12345678 /* now 16*4=64 */
.global _end_vect
_end_vect:
.balignl 16,0xdeadbeef
2.接下来我们会放一个代码段地址值,这个值放在
Config.mk(uboot1.1.16_256m-for36---v1.01\board\samsung\smdk6410):TEXT_BASE= 0xc7e00000中:
_TEXT_BASE:
.word TEXT_BASE
3.接下来我们放一个MMU对应的实际物理首地址,这个值放在
Smdk6410.h(uboot1.1.16_256m-for36---v1.01\include\configs)中:
#define CFG_PHY_UBOOT_BASE MEMORY_BASE_ADDRESS + 0x7e00000
#define MEMORY_BASE_ADDRESS 0x50000000
_TEXT_PHY_BASE: /*因此MMU对应的实际内存首地址值就是0x57e00000*/
.word CFG_PHY_UBOOT_BASE
4.接下来我们存放一个跳转地址,这个地址是uboot结束所有汇编的初始化操作后,最后要跳转到的C函数的地址值,相当于我们平时最熟悉的main函数了。
.globl _armboot_start
_armboot_start:
.word _start
5.接下来我们存放bss数据段的起始和结束地址值,把这些地址都放在uboot的头部,是为了保证在重定位之前,不会访问到超过s3c6410的8K字节的引导存储区。如果这些值不在头部定义,编译器就会随机为这些值分配地址了。
.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:
.word0x0badc0de
/* IRQ stack memory (calculated at run-time) */
.globl FIQ_STACK_START
FIQ_STACK_START:
.word 0x0badc0de
#endif
7接下来我们就进入到了复位时将会跳转到的地方,reset地址,这里我们首先配置程序状态寄存器cpsr进入到SVC用户管理模式
mrs r0,cpsr
bic r0,r0,#0x1f
orr r0,r0,#0xd3
msr cpsr,r0
8.然后进入到CPU初始化cpu_init_crit,这里我们首先开启了I/D cashes,以大幅度提高uboot程序的执行效率,并且关闭MMU,进行外设端口地址映射,这些指令都是armcore1176核说明中规定的,没有什么可说的。
cpu_init_crit:
/*
* flush v4 I/D 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
/* Peri port setup */
ldr r0, =0x70000000
orr r0, r0, #0x13
mcr p15,0,r0,c15,c2,4 @ 256M(0x70000000-0x7fffffff)
9.接下来我们会通过bl lowlevel_init进入到
lowlevel_init.S (uboot1.1.16_256m-for36---v1.01\board\samsung\smdk6410) 中进行板级底层的初始化。在这个里面我们会根据Smdk6410.h (uboot1.1.16_256m-for36---v1.01\
include\configs)中宏的配置情况,分别进行不同底层硬件的初始化,如我的uboot中就有以下一些初始化,而以下寄存器地址相关的宏则几乎都源自于S3c6410.h (uboot1.1.16_256m-for36---v1.01\include)文件,这里就不一一展开了:
lowlevel_init:
①一些GPIO和外围接口的初始化
mov r12, lr
ldr r0, =ELFIN_GPIO_BASE /*0x7f008000*/
ldr r1, =0x55555555
str r1, [r0, #GPKCON0_OFFSET] /*#define GPKCON0_OFFSET 0x800 (DATA_CF)*/
ldr r1, =0x55555555
str r1, [r0, #GPKCON1_OFFSET] /*#define GPKCON1_OFFSET 0x804*/
ldr r1, =0x22222666
str r1, [r0, #GPLCON0_OFFSET] /*GPL7-GPL3 HOST I/F ADDR GPL0-GPL2 ADDR_CF*/
ldr r1, =0x04000000
str r1, [r0, #GPFCON_OFFSET] /*GPF13 output*/
ldr r1, =0x2000
str r1, [r0, #GPFDAT_OFFSET] /*GPF13 pin High enable OTG*/
/* LED on only #8 */
ldr r0, =ELFIN_GPIO_BASE
ldr r1, =0x00111111
str r1, [r0, #GPMCON_OFFSET] /*GPM0-GPM5 output*/
ldr r1, =0x00000555
str r1, [r0, #GPMPUD_OFFSET] /*下拉GPM*/
ldr r1, =0x002a
str r1, [r0, #GPMDAT_OFFSET] /*101010 隔着亮LED1-LED4*/
ldr r1, =0 /*0x55555555 phantom*/
str r1, [r0, #MEM1DRVCON_OFFSET] /*0x1D4 存储器端口1管脚初始化*/
②关看门狗
/* Disable Watchdog */
ldr r0, =0x7e000000 @0x7e004000
orr r0, r0, #0x4000
mov r1, #0
str r1, [r0]
③关中断和清标识位
@ External interrupt pending clear
ldr r0, =(ELFIN_GPIO_BASE+EINTPEND_OFFSET) /*EINTPEND 0x924*/
ldr r1, [r0]
str r1, [r0]
ldr r0, =ELFIN_VIC0_BASE_ADDR @0x71200000
ldr r1, =ELFIN_VIC1_BASE_ADDR @0x71300000
@ Disable all interrupts (VIC0 and VIC1)
mvn r3, #0x0
str r3, [r0, #oINTMSK] /*0x14 */
str r3, [r1, #oINTMSK] /*0x14 */
@ Set all interrupts as IRQ
mov r3, #0x0
str r3, [r0, #oINTMOD] /*0xC */
str r3, [r1, #oINTMOD]
@ Pending Interrupt Clear
mov r3, #0x0
str r3, [r0, #oVECTADDR] /*0xF00 clear VICxADDRESS*/
str r3, [r1, #oVECTADDR]
④接下来初始化系统时钟
/* init system clock */
bl system_clock_init /*CONFIG_CLK_532_133_66*/
相应的实现也在lowlevel_init.S (uboot1.1.16_256m-for36---v1.01\board\samsung\smdk6410)中
system_clock_init:
ldr r0, =ELFIN_CLOCK_POWER_BASE @0x7e00f000 /*#define ELFIN_CLOCK_POWER_BASE 0x7e00f000*/
#ifdef CONFIG_SYNC_MODE /*同步异步模式切换宏,在Smdk6410.h中定义*/
/*CONFIG_CLK_532_133_66*/
ldr r1, [r0, #OTHERS_OFFSET] /*OTHERS_OFFSET 0x900*/
mov r2, #0x40
orr r1, r1, r2
str r1, [r0, #OTHERS_OFFSET] /*set APLL*/
nop
nop
nop
nop
nop
ldr r2, =0x80
orr r1, r1, r2
str r1, [r0, #OTHERS_OFFSET] /*set Synchronous*/
check_syncack:
ldr r1, [r0, #OTHERS_OFFSET]
ldr r2, =0xf00
and r1, r1, r2
cmp r1, #0xf00
bne check_syncack /*check Synchronous status*/
#else /* ASYNC Mode */
nop
nop
nop
nop
nop
ldr r1, [r0, #OTHERS_OFFSET] /*set ASynchronous*/
bic r1, r1, #0xC0
orr r1, r1, #0x40
str r1, [r0, #OTHERS_OFFSET]
wait_for_async: /*check ASynchronous status*/
ldr r1, [r0, #OTHERS_OFFSET]
and r1, r1, #0xf00
cmp r1, #0x0
bne wait_for_async
ldr r1, [r0, #OTHERS_OFFSET] /*set MPLL*/
bic r1, r1, #0x40
str r1, [r0, #OTHERS_OFFSET]
#endif
mov r1, #0xff00
orr r1, r1, #0xff
str r1, [r0, #APLL_LOCK_OFFSET] /*时钟稳定时间*/
str r1, [r0, #MPLL_LOCK_OFFSET]
str r1, [r0, #EPLL_LOCK_OFFSET]
/* CLKUART(=66.5Mhz) = CLKUART_input(532/2=266Mhz) / (UART_RATIO(3)+1) */
/* CLKUART(=50Mhz) = CLKUART_input(400/2=200Mhz) / (UART_RATIO(3)+1) */
/* Now, When you use UART CLK SRC by EXT_UCLK1, We support 532MHz & 400MHz value */
#if defined(CONFIG_CLKSRC_CLKUART) /*设置串口时钟*/
ldr r1, [r0, #CLK_DIV2_OFFSET]
bic r1, r1, #0xf0000
orr r1, r1, #0x30000
str r1, [r0, #CLK_DIV2_OFFSET] /*UART_RATIO=3*/
#endif
/*#define APLL_MDIV 266
#define APLL_PDIV 3
#define APLL_SDIV 1*/
ldr r1, [r0, #CLK_DIV0_OFFSET] /*设置时钟分频器和时钟源的选择*/
bic r1, r1, #0x30000
bic r1, r1, #0xff00
bic r1, r1, #0xff
/*#define CLK_DIV_VAL ((Startup_PCLKdiv<<12)|(Startup_HCLKx2div<<9)
|(Startup_HCLKdiv<<8)|(Startup_MPLLdiv<<4)|Startup_APLLdiv)*/
ldr r2, =CLK_DIV_VAL
orr r1, r1, r2
str r1, [r0, #CLK_DIV0_OFFSET]
ldr r1, =APLL_VAL
str r1, [r0, #APLL_CON_OFFSET]
ldr r1, =MPLL_VAL
str r1, [r0, #MPLL_CON_OFFSET]
ldr r1, =0x80200203 /* FOUT of EPLL is 24MHz */
str r1, [r0, #EPLL_CON0_OFFSET]
ldr r1, =0x0
str r1, [r0, #EPLL_CON1_OFFSET]
ldr r1, [r0, #CLK_SRC_OFFSET] /*0x1c, APLL, MPLL, EPLL select to Fout */
#if defined(CONFIG_CLKSRC_CLKUART)
ldr r2, =0x2007
#else
ldr r2, =0x7
#endif
orr r1, r1, r2
str r1, [r0, #CLK_SRC_OFFSET] /*切换时钟源*/
/* wait at least 200us to stablize all clock */
mov r1, #0x10000
1: subs r1, r1, #1
bne 1b
#if 0
mrc p15, 0, r0, c1, c0, 0
orr r0, r0, #0xc0000000 /* clock setting in MMU */
mcr p15, 0, r0, c1, c0, 0
#endif
#ifdef CONFIG_SYNC_MODE /* Synchronization for VIC port */
ldr r1, [r0, #OTHERS_OFFSET]
orr r1, r1, #0x20
str r1, [r0, #OTHERS_OFFSET]
#else
ldr r1, [r0, #OTHERS_OFFSET]
bic r1, r1, #0x20
str r1, [r0, #OTHERS_OFFSET]
#endif
mov pc, lr
⑤接下来回到lowlevel_init中初始化串口bl uart_asm_init,可以用来在前期打印一些调试信息。
uart_asm_init:
/* set GPIO to enable UART */
@ GPIO setting for UART
ldr r0, =ELFIN_GPIO_BASE /*0x7F008000*/
ldr r1, =0x220022 /*pin uart0 uart1*/
str r1, [r0, #GPACON_OFFSET]
ldr r1, =0x2222 /*pin uart2 uart3*/
str r1, [r0, #GPBCON_OFFSET]
ldr r0, =ELFIN_UART_CONSOLE_BASE @0x7F005000
mov r1, #0x0
str r1, [r0, #UFCON_OFFSET]
str r1, [r0, #UMCON_OFFSET]
mov r1, #0x3 @was 0. /*8-bit*/
str r1, [r0, #ULCON_OFFSET]
ldr r1, =0x245 /* UARTCLK SRC = x0 => PCLK */
str r1, [r0, #UCON_OFFSET]
#if defined(CONFIG_UART_50)
ldr r1, =0x1A
#elif defined(CONFIG_UART_66)
ldr r1, =0x35 /*6410*/
#else
ldr r1, =0x1A
#endif
str r1, [r0, #UBRDIV_OFFSET] /*ldr r1, =0x22*/
#if defined(CONFIG_UART_50)
ldr r1, =0x3
#elif defined(CONFIG_UART_66)
ldr r1, =0x1
#else
ldr r1, =0x3
#endif
str r1, [r0, #UDIVSLOT_OFFSET] /*ldr r1, =0x1FFF*/
ldr r1, =0x4f4f4f4f
str r1, [r0, #UTXH_OFFSET] @'O'
mov pc, lr
初始化完成后我们回到lowlevel_init中打印一些调试信息
ldr r0, =ELFIN_UART_BASE
ldr r1, =0x4b4b4b4b
str r1, [r0, #UTXH_OFFSET]
⑥如果是从nandflash启动,则可利用Smdk6410.h中的宏使能nand的初始化
#if defined(CONFIG_NAND)
/* simple init for NAND */
bl nand_asm_init
#endif
/*
* Nand Interface Init for SMDK6400 */
nand_asm_init:
ldr r0, =ELFIN_NAND_BASE
ldr r1, [r0, #NFCONF_OFFSET]
orr r1, r1, #0x70
orr r1, r1, #0x7700
str r1, [r0, #NFCONF_OFFSET]
ldr r1, [r0, #NFCONT_OFFSET]
orr r1, r1, #0x03
str r1, [r0, #NFCONT_OFFSET]
mov pc, lr
⑦接下来我们初始化内存bl mem_ctrl_asm_init,我的U-boot源码将内存代码放在了cpu_init.S (uboot1.1.16_256m-for36---v1.01\cpu\s3c64xx\s3c6410)中
mem_ctrl_asm_init:
ldr r0, =ELFIN_MEM_SYS_CFG @Memory sussystem address 0x7e00f120
mov r1, #0xd @ Xm0CSn2 = NFCON CS0, Xm0CSn3 = NFCON CS1
str r1, [r0]
ldr r0, =ELFIN_DMC1_BASE @DMC1 base address 0x7e001000
ldr r1, =0x04
str r1, [r0, #INDEX_DMC_MEMC_CMD]
ldr r1, =DMC_DDR_REFRESH_PRD
str r1, [r0, #INDEX_DMC_REFRESH_PRD]
ldr r1, =DMC_DDR_CAS_LATENCY
str r1, [r0, #INDEX_DMC_CAS_LATENCY]
ldr r1, =DMC_DDR_t_DQSS
str r1, [r0, #INDEX_DMC_T_DQSS]
ldr r1, =DMC_DDR_t_MRD
str r1, [r0, #INDEX_DMC_T_MRD]
ldr r1, =DMC_DDR_t_RAS
str r1, [r0, #INDEX_DMC_T_RAS]
ldr r1, =DMC_DDR_t_RC
str r1, [r0, #INDEX_DMC_T_RC]
ldr r1, =DMC_DDR_t_RCD
ldr r2, =DMC_DDR_schedule_RCD
orr r1, r1, r2
str r1, [r0, #INDEX_DMC_T_RCD]
ldr r1, =DMC_DDR_t_RFC
ldr r2, =DMC_DDR_schedule_RFC
orr r1, r1, r2
str r1, [r0, #INDEX_DMC_T_RFC]
ldr r1, =DMC_DDR_t_RP
ldr r2, =DMC_DDR_schedule_RP
orr r1, r1, r2
str r1, [r0, #INDEX_DMC_T_RP]
ldr r1, =DMC_DDR_t_RRD
str r1, [r0, #INDEX_DMC_T_RRD]
ldr r1, =DMC_DDR_t_WR
str r1, [r0, #INDEX_DMC_T_WR]
ldr r1, =DMC_DDR_t_WTR
str r1, [r0, #INDEX_DMC_T_WTR]
ldr r1, =DMC_DDR_t_XP
str r1, [r0, #INDEX_DMC_T_XP]
ldr r1, =DMC_DDR_t_XSR
str r1, [r0, #INDEX_DMC_T_XSR]
ldr r1, =DMC_DDR_t_ESR
str r1, [r0, #INDEX_DMC_T_ESR]
ldr r1, =DMC1_MEM_CFG
str r1, [r0, #INDEX_DMC_MEMORY_CFG]
ldr r1, =DMC1_MEM_CFG2
str r1, [r0, #INDEX_DMC_MEMORY_CFG2]
ldr r1, =DMC1_CHIP0_CFG
str r1, [r0, #INDEX_DMC_CHIP_0_CFG]
ldr r1, =DMC_DDR_32_CFG
str r1, [r0, #INDEX_DMC_USER_CONFIG]
@DMC0 DDR Chip 0 configuration direct command reg
ldr r1, =DMC_NOP0
str r1, [r0, #INDEX_DMC_DIRECT_CMD]
@Precharge All
ldr r1, =DMC_PA0
str r1, [r0, #INDEX_DMC_DIRECT_CMD]
@Auto Refresh 2 time
ldr r1, =DMC_AR0
str r1, [r0, #INDEX_DMC_DIRECT_CMD]
str r1, [r0, #INDEX_DMC_DIRECT_CMD]
@MRS
ldr r1, =DMC_mDDR_EMR0
str r1, [r0, #INDEX_DMC_DIRECT_CMD]
@Mode Reg
ldr r1, =DMC_mDDR_MR0
str r1, [r0, #INDEX_DMC_DIRECT_CMD]
#ifdef CONFIG_SMDK6410_X5A
ldr r1, =DMC1_CHIP1_CFG
str r1, [r0, #INDEX_DMC_CHIP_1_CFG]
@DMC0 DDR Chip 0 configuration direct command reg
ldr r1, =DMC_NOP1
str r1, [r0, #INDEX_DMC_DIRECT_CMD]
@Precharge All
ldr r1, =DMC_PA1
str r1, [r0, #INDEX_DMC_DIRECT_CMD]
@Auto Refresh 2 time
ldr r1, =DMC_AR1
str r1, [r0, #INDEX_DMC_DIRECT_CMD]
str r1, [r0, #INDEX_DMC_DIRECT_CMD]
@MRS
ldr r1, =DMC_mDDR_EMR1
str r1, [r0, #INDEX_DMC_DIRECT_CMD]
@Mode Reg
ldr r1, =DMC_mDDR_MR1
str r1, [r0, #INDEX_DMC_DIRECT_CMD]
#endif
@Enable DMC1
mov r1, #0x0
str r1, [r0, #INDEX_DMC_MEMC_CMD]
check_dmc1_ready:
ldr r1, [r0, #INDEX_DMC_MEMC_STATUS]
mov r2, #0x3
and r1, r1, r2
cmp r1, #0x1
bne check_dmc1_ready
nop
mov pc, lr
这里面主要是初始化s3c6410的内存控制器,配置相关内存芯片的一些时序参数和初始化的过程,这里由于各人选取的外设都是不同的,作者只想讲解uboot整体架构相关的内容,对于外设的初始化等内容就不细述了,而上述所有寄存器相关的宏的信息依然是存放在s3c6410.h文件中。
⑧接下来我们会判断复位唤醒的一个标志,如果唤醒了,就会调用INFORM0(ELFIN_CLOCK_POWER_BASE+INF_REG0_OFFSET)寄存器中用户自定义的一个函数地址,并跳转过去执行。否则我们在lowlevel_init中该做的事情到这里就全部结束了,我们将会回到start.S中继续往后执行。
#if 1
ldr r0, =(ELFIN_CLOCK_POWER_BASE+RST_STAT_OFFSET)
ldr r1, [r0]
bic r1, r1, #0xfffffff7
cmp r1, #0x8
beq wakeup_reset
#endif
1:
ldr r0, =ELFIN_UART_BASE
ldr r1, =0x4b4b4b4b
str r1, [r0, #UTXH_OFFSET]
mov lr, r12
mov pc, lr
#if 1
wakeup_reset:
/*Clear wakeup status register*/
ldr r0, =(ELFIN_CLOCK_POWER_BASE+WAKEUP_STAT_OFFSET)
ldr r1, [r0]
str r1, [r0]
/*LED test*/
ldr r0, =ELFIN_GPIO_BASE
ldr r1, =0x3000
str r1, [r0, #GPNDAT_OFFSET]
/*Load return address and jump to kernel*/
ldr r0, =(ELFIN_CLOCK_POWER_BASE+INF_REG0_OFFSET)
ldr r1, [r0] /* r1 = physical address of s3c6400_cpu_resume function*/
mov pc, r1 /*Jump to kernel (sleep-s3c6400.S)*/
nop
nop
#endif
10.从lowlevel_init中回来,这里我们做的第一件事情就是将程序拷贝到ram中执行。
ldr r0, =0xff000fff
bic r1, pc, r0 /* r0 <- current base addr of code */
ldr r2, _TEXT_BASE /* r1 <- original base addr in ram */
bic r2, r2, r0 /* r0 <- current base addr of code */
cmp r1, r2 /* compare r0, r1 */
beq after_copy /* r0 == r1 then skip flash copy */
#ifdef CONFIG_BOOT_NOR /* relocate U-Boot to RAM */
adr r0, _start /* r0 <- current position of code */
ldr r1, _TEXT_PHY_BASE /* r1 <- destination */
ldr r2, _armboot_start
ldr r3, _bss_start
sub r2, r3, r2 /* r2 <- size of armboot */
add r2, r0, r2 /* r2 <- source end address */
nor_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 nor_copy_loop
b after_copy
#endif
#ifdef CONFIG_BOOT_NAND
mov r0, #0x1000
bl copy_from_nand
#endif
#ifdef CONFIG_BOOT_MOVINAND
ldr sp, _TEXT_PHY_BASE
bl movi_bl2_copy
b after_copy
#endif
#ifdef CONFIG_BOOT_ONENAND
ldr sp, =0x50000000 @ temporary stack
#ifdef CONFIG_S3C6400
mov r1, =0x20000000 @ start buffer register
orr r1, r1, #0xc30000
orr r1, r1, #0xc800
#else
mov r1, #0x23000000 @ start buffer register
orr r1, r1, #0x30000
orr r1, r1, #0xc800
#endif
ldr r2, [r1, #0x84] @ ecc bypass
orr r2, r2, #0x100
str r2, [r1, #0x84]
sub r0, r1, #0x0400 @ start address1 register
str r3, [r0, #0x00]
str r3, [r0, #0x04] @ select dataram for DDP as 0
mov r4, #0x104 @ interrupt register
mov r6, #0x0c00 @ fixed dataram1 sector number
str r6, [r1, #0x00]
mov r3, #0x0 @ DFS, FBA
mov r5, #0x0000 @ FPA, FSA
ldr r9, =CFG_PHY_UBOOT_BASE @ destination
onenand_bl2_load:
str r3, [r0, #0x00] @ save DFS, FBA
str r5, [r0, #0x1c] @ save FPA, FSA
mov r7, #0x0 @ clear interrupt
str r7, [r1, r4]
str r7, [r1, #0x80] @ write load command
mov r8, #0x1000
onenand_wait_loop2:
subs r8, r8, #0x1
bne onenand_wait_loop2
onenand_wait_int: @ wait INT and RI
ldr r7, [r1, r4]
mov r8, #0x8000
orr r8, r8, #0x80
tst r7, r8
beq onenand_wait_int
mov r7, #0x0 @ clear interrupt
str r7, [r1, r4]
mov r8, #0xc00 @ source address (dataram1)
mov r10, #0x40 @ copy loop count (64 = 2048 / 32)
stmia sp, {r0-r7} @ backup
onenand_copy_to_ram:
ldmia r8!, {r0-r7}
stmia r9!, {r0-r7}
subs r10, r10, #0x1
bne onenand_copy_to_ram
ldmia sp, {r0-r7} @ restore
add r5, r5, #0x4 @ next FPA
cmp r5, #0x100 @ last FPA?
bne onenand_bl2_load
/* next block */
mov r5, #0x0 @ reset FPA
add r3, r3, #0x1 @ next FBA
cmp r3, #0x2 @ last FBA?
bne onenand_bl2_load
b after_copy
#endif
#ifdef CONFIG_BOOT_ONENAND_IROM
ldr sp, _TEXT_PHY_BASE
bl onenand_bl2_copy
b after_copy
#endif
根据smdk6410.h中所配置的宏的不同,我们将从NOR启动或NAND启动或ONENAND启动。
11.根据smdk6410.h中宏CONFIG_ENABLE_MMU的配置使能MMU
after_copy:
#ifdef CONFIG_ENABLE_MMU
enable_mmu:
/* enable domain access */
ldr r5, =0x0000ffff
mcr p15, 0, r5, c3, c0, 0 @ load domain access register
/* Set the TTB register */
ldr r0, _mmu_table_base
ldr r1, =CFG_PHY_UBOOT_BASE
ldr r2, =0xfff00000
bic r0, r0, r2
orr r1, r0, r1
mcr p15, 0, r1, c2, c0, 0
/* Enable the MMU */
mmu_on:
mrc p15, 0, r0, c1, c0, 0
orr r0, r0, #1 /* Set CR_M to enable MMU */
mcr p15, 0, r0, c1, c0, 0
nop
nop
nop
nop
#endif
12.如果后续使用了C函数,当然要设置栈地址,如果使用了堆、bdinfo还要为他们留出空间。
skip_hw_init:
/* Set up the stack */
stack_setup:
#ifdef CONFIG_MEMORY_UPPER_CODE
/*CONFIG_MEMORY_UPPER_CODE决定了数据是存放在uboot空间的顶部还是uboot代码的下面*/
ldr sp, =(CFG_UBOOT_BASE + CFG_UBOOT_SIZE - 0xc)
#else
ldr r0, _TEXT_BASE /* upper 128 KiB: relocated uboot */
sub r0, r0, #CFG_MALLOC_LEN /* malloc area */
sub r0, r0, #CFG_GBL_DATA_SIZE /* bdinfo */
#ifdef CONFIG_USE_IRQ
sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
#endif
sub sp, r0, #12 /* leave 3 words for abort-stack */
#endif
13.清bss段数据
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
14.接下来我们终于进入了uboot的重点部分了,就是C环境下的内容,一条伪汇编指令直接跳转到ram中执行后续的内容。
ldr pc, _start_armboot
_start_armboot:
.word start_armboot
这里我们uboot汇编部分就全部结束了,从上面我们也可以看出所有配置类的宏(如CONFIG_BOOT_MOVINAND等以CONFIG开头的宏)基本上都是在Smdk6410.h (uboot1.1.16_256m-for36---v1.01\include\configs)中定义的,而寄存器地址相关的宏都集中在S3c6410.h (uboot1.1.16_256m-for36---v1.01\include)中,因此在第二章中我们不再赘述这两类宏的定义来源。
第二章 Uboot1.1.16中的C环境部分
显然我们的C环境下入口函数就是Board.c (uboot1.1.16_256m-for36---v1.01\lib_arm) 中的start_armboot函数。
1.在这个函数的头部有一个地址值gd_base,它是结构体gd_t 的基地址,因此决定了gd_t 存放的位置,gd_t结构体定义在Global_data.h (uboot1.1.16_256m-for36---v1.01\include\asm-arm)中,其基地址值就从下面代码中来。
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
#if defined(CONFIG_BOOT_MOVINAND)
uint *magic = (uint *) (PHYS_SDRAM_1);
#endif
/* Pointer is writable since we allocated a register for it */
#ifdef CONFIG_MEMORY_UPPER_CODE /* by scsuh */
ulong gd_base;
gd_base = CFG_UBOOT_BASE + CFG_UBOOT_SIZE - CFG_MALLOC_LEN - CFG_STACK_SIZE - sizeof(gd_t);
#ifdef CONFIG_USE_IRQ
gd_base -= (CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ);
#endif
gd = (gd_t*)gd_base;
#else
gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t));
#endif
其中gd_t结构体的定义如下:
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;
相应的bd_t定义在文件U-boot.h (uboot1.1.16_256m-for36---v1.01\include\asm-arm) 中:
typedef struct bd_info {
int bi_baudrate; /* serial console baudrate */
unsigned long bi_ip_addr; /* IP Address */
unsigned char bi_enetaddr[6]; /* Ethernet adress */
struct environment_s *bi_env;
ulong bi_arch_number; /* unique id for this board */
ulong bi_boot_params; /* where this board expects params */
struct /* RAM configuration */
{
ulong start;
ulong size;
} bi_dram[CONFIG_NR_DRAM_BANKS];
#ifdef CONFIG_HAS_ETH1
/* second onboard ethernet port */
unsigned char bi_enet1addr[6];
#endif
} bd_t;
在我的uboot代码的smdk6410.h中,使能了MMU后UBOOT的基地址是不同的。
#ifdef CONFIG_ENABLE_MMU
#define CFG_UBOOT_BASE 0xc7e00000
#else
#define CFG_UBOOT_BASE 0x57e00000
#endif
这里我们为整个Uboot留出空间的是2MB
#define CFG_UBOOT_SIZE (2*1024*1024)
在Uboot的末端我们将环境变量和512kb空间留给了堆
#define CFG_MALLOC_LEN (CFG_ENV_SIZE + 512*1024)
#define CFG_ENV_SIZE 0x80000 /* Total Size of Environment Sector */
我们将堆前面的512Kb字节给了栈
#define CFG_STACK_SIZE 512*1024
如果有使用IRQ和快速IRQ中断,还要为中断处理留出一定的栈空间,这里是4kb
#ifdef CONFIG_USE_IRQ
#define CONFIG_STACKSIZE_IRQ (4*1024) /* IRQ stack */
#define CONFIG_STACKSIZE_FIQ (4*1024) /* FIQ stack */
#endif
于是,我们将gd_t 结构体就存放在以上这些的下面,当然gd_t 的基地址还要减去它自身所占空间的大小。如果没有定义CONFIG_MEMORY_UPPER_CODE 宏,那么我们将堆存放在_armboot_start代码之下,并将gd_t 存放在堆的下面。
2.然后我们将gd对象的内容清除,并在gd之前存放结构体bd_t,同时bd_t的内容也被清除。
将bd_t的地址存放在gd结构体的bd成员中。
memset ((void*)gd, 0, sizeof (gd_t));
gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));
memset (gd->bd, 0, sizeof (bd_t));
monitor_flash_len = _bss_start - _armboot_start; //这个值会用作后面flash_protect的保护长度
3.接下来我们对板上的资源在C环境下进行更为复杂的初始化,我们调用初始化序列数组init_sequence。
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
if ((*init_fnc_ptr)() != 0) {
hang ();
}
}
这个init_sequence数组被定义在Board.c (uboot1.1.16_256m-for36---v1.01\lib_arm)中,如果序列中的函数返回值不为0,那么将挂死uboot,并打印错误。否则,我们依次调用初始序列中的函数。
init_fnc_t *init_sequence[] = {
cpu_init, /* basic cpu dependent setup */
board_init, /* basic board dependent setup */
interrupt_init, /* set up exceptions */
env_init, /* initialize environment */
init_baudrate, /* initialze baudrate settings */
serial_init, /* serial communications setup */
console_init_f, /* stage 1 init of console */
display_banner, /* say that we are here */
#if defined(CONFIG_DISPLAY_CPUINFO)
print_cpuinfo, /* display cpu info (and speed) */
#endif
#if defined(CONFIG_DISPLAY_BOARDINFO)
checkboard, /* display board info */
#endif
dram_init, /* configure available RAM banks */
display_dram_config,
NULL,
};
①首先我们调用的是cpu_init,它位于Cpu.c (uboot1.1.16_256m-for36---v1.01\cpu\s3c64xx)中。
#ifdef CONFIG_USE_IRQ
IRQ_STACK_START = _armboot_start - CFG_MALLOC_LEN-CFG_GBL_DATA_SIZE - 4;
FIQ_STACK_START = IRQ_STACK_START - CONFIG_STACKSIZE_IRQ;
#endif
return 0; //必须返回0,否则uboot会被挂起
如果我们使用了IRQ中断,就赋值这两个变量,这两个变量正是我们在汇编刚开始保存的两个关于IRQ中断的变量。第一个值是以_armboot_start为基值,留出CFG_MALLOC_LEN堆的空间,此外留出128字节初始化数据,再减去4字节空间。FIQ中断的栈基地址则取IRQ栈基地址偏移4K字节的位置,即留出4K字节给IRQ作为栈空间。
#define CFG_GBL_DATA_SIZE 128 /* size in bytes reserved for initial data */
②然后调用board_init,它位于
Smdk6410.c (uboot1.1.16_256m-for36---v1.01\board\samsung\smdk6410)中,当中首先声明一个不能被编译器占用的寄存器变量来存储gd
#define DECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm ("r8")
该宏位于Global_data.h (uboot1.1.16_256m-for36---v1.01\include\asm-arm):中
然后调用cs8900_pre_init初始化以太网控制器
static void cs8900_pre_init(void) //也在Smdk6410.c中
{
SROM_BW_REG &= ~(0xf << 4);
SROM_BW_REG |= (1<<7) | (1<<6) | (1<<4);
SROM_BC1_REG= ((CS8900_Tacs<<28)+(CS8900_Tcos<<24)+(CS8900_Tacc<<16)
+(CS8900_Tcoh<<12)+(CS8900_Tah<<8)+(CS8900_Tacp<<4)+(CS8900_PMC));
}
在S3c6410.h (uboot1.1.16_256m-for36---v1.01\include)中定义了宏
/*
* Memory controller
*/
#define ELFIN_SROM_BASE 0x70000000
#define SROM_BW_REG __REG(ELFIN_SROM_BASE+0x0)
#define SROM_BC0_REG __REG(ELFIN_SROM_BASE+0x4)
#define SROM_BC1_REG __REG(ELFIN_SROM_BASE+0x8)
#define SROM_BC2_REG __REG(ELFIN_SROM_BASE+0xC)
#define SROM_BC3_REG __REG(ELFIN_SROM_BASE+0x10)
#define SROM_BC4_REG __REG(ELFIN_SROM_BASE+0x14)
#define SROM_BC5_REG __REG(ELFIN_SROM_BASE+0x18)
而在Smdk6410.c (uboot1.1.16_256m-for36---v1.01\board\samsung\smdk6410)中定义了宏:
#define CS8900_Tacs (0x0) // 0clk address set-up
#define CS8900_Tcos (0x4) // 4clk chip selection set-up
#define CS8900_Tacc (0xE) // 14clk access cycle
#define CS8900_Tcoh (0x1) // 1clk chip selection hold
#define CS8900_Tah (0x4) // 4clk address holding time
#define CS8900_Tacp (0x6) // 6clk page mode access cycle
#define CS8900_PMC (0x0) // normal(1data)page mode configuration
接下来存储板级信息到gd_t,bd_t的对应结构体中,其中包含机器号,和boot操作系统时用到的256字节的参数首地址值。
gd->bd->bi_arch_number = MACH_TYPE;
gd->bd->bi_boot_params = (PHYS_SDRAM_1+0x100);
在Smdk6410.h (uboot1.1.16_256m-for36---v1.01\include\configs)中定义了宏:
#define MEMORY_BASE_ADDRESS 0x50000000
#define MACH_TYPE 1626 //机器类型号
#define PHYS_SDRAM_1 MEMORY_BASE_ADDRESS /* SDRAM Bank #1 */
③接下来调用interrupt_init,它位于Interrupts.c (uboot1.1.16_256m-for36---v1.01\cpu\s3c64xx)中,
S3C64XX_TIMERS *const timers = S3C64XX_GetBase_TIMERS();
/* use PWM Timer 4 because it has no output */
/* prescaler for Timer 4 is 16 */
timers->TCFG0 = 0x0f00; //死区时间最大,定时器分频系数最小
if (timer_load_val == 0) { //这个值在Interrupts.c中初始为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;//设置计数寄存器,如果PCLK符合 //以上标准,则为10ms,lastdec记录分频系数
/* auto load, manual update of Timer 4 */
timers->TCON = (timers->TCON & ~0x00700000) | TCON_4_AUTO | TCON_4_UPDATE;
/* auto load, start Timer 4 */
timers->TCON = (timers->TCON & ~0x00700000) | TCON_4_AUTO | COUNT_4_ON;
timestamp = 0; //设置自动重装,手动更新,并启动了定时器4
对于上面的源码S3C64XX_TIMERS定时器结构体在S3c64x0.h (uboot1.1.16_256m-for36---v1.01\include) 中定义。
typedef struct {
S3C64XX_REG32 TCFG0;
S3C64XX_REG32 TCFG1;
S3C64XX_REG32 TCON;
S3C64XX_TIMER ch[4];
S3C64XX_REG32 TCNTB4;
S3C64XX_REG32 TCNTO4;
} /*__attribute__((__packed__))*/ S3C64XX_TIMERS;
//-------------------------S3c6410.h (uboot1.1.16_256m-for36---v1.01\include)--------------------------
static inline S3C64XX_TIMERS * S3C64XX_GetBase_TIMERS(void)
{
return (S3C64XX_TIMERS *)ELFIN_TIMER_BASE;
}
该文件中还定义了相关寄存器地址的宏
/*
* PWM timer
*/
#define ELFIN_TIMER_BASE 0x7F006000
#define TCFG0_REG __REG(0x7F006000)
#define TCFG1_REG __REG(0x7F006004)
#define TCON_REG __REG(0x7F006008)
#define TCNTB0_REG __REG(0x7F00600c)
#define TCMPB0_REG __REG(0x7F006010)
#define TCNTO0_REG __REG(0x7F006014)
#define TCNTB1_REG __REG(0x7F006018)
#define TCMPB1_REG __REG(0x7F00601c)
#define TCNTO1_REG __REG(0x7F006020)
#define TCNTB2_REG __REG(0x7F006024)
#define TCMPB2_REG __REG(0x7F006028)
#define TCNTO2_REG __REG(0x7F00602c)
#define TCNTB3_REG __REG(0x7F006030)
#define TCMPB3_REG __REG(0x7F006034)
#define TCNTO3_REG __REG(0x7F006038)
#define TCNTB4_REG __REG(0x7F00603c)
#define TCNTO4_REG __REG(0x7F006040)
有了以上内容我们可以看出,这段源码主要功能就是获得PWM寄存器的基地址,并对定时器进行相关初始化,关于这里面的计算没什么可说的,就是访问寄存器并根据手册提供的方法计算PCLK。
//---------------------Speed.c (uboot1.1.16_256m-for36---v1.01\cpu\s3c64xx\s3c6410)-----------------
/* return PCLK frequency */
ulong get_PCLK(void)
{
ulong fclk;
uint hclkx2_div = ((CLK_DIV0_REG>>9) & 0x7) + 1; //得到HCLKX2_RATIO
uint pre_div = ((CLK_DIV0_REG>>12) & 0xf) + 1; //得到PCLK_RATIO
if(OTHERS_REG & 0x80) //同步模式还是异步模式
fclk = get_FCLK(); // SYNC Mode
else
fclk = get_PLLCLK(MPLL); // ASYNC Mode
return fclk/(hclkx2_div * pre_div);
}
④接下来调用env_init,它位于Env_nowhere.c (uboot1.1.16_256m-for36---v1.01\common) :
gd->env_addr = (ulong)&default_environment[0]; //取默认环境变量的存放地址
gd->env_valid = 0;
变量default_environment定义在Env_common.c (uboot1.1.16_256m-for36---v1.01\common)中
static uchar default_environment[] = {“...”“...”};
⑤然后调用init_baudrate,它位于Board.c (uboot1.1.16_256m-for36---v1.01\lib_arm)中
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;
//simple_strtoul在Utils.c (uboot1.1.16_256m-for36---v1.01\tools\updater)
//simple_strtoul就是将字符串转换成数字
其实这个函数就是从环境变量default_environment中取出baudrate信息,并存放在gd_t和bd_t结构体中,如果default_environment中没有这个信息,就直接从头文件Smdk6410.h中取宏CONFIG_BAUDRATE的值作为波特率。
//-------------------Cmd_nvedit.c (uboot1.1.16_256m-for36---v1.01\common)---------------------------
int getenv_r (char *name, char *buf, unsigned len)
{
int i, nxt;
for (i=0; env_get_char(i) != '\0'; i=nxt+1) {
int val, n;
for (nxt=i; env_get_char(nxt) != '\0'; ++nxt) {
if (nxt >= CFG_ENV_SIZE) {
return (-1);
}
}
if ((val=envmatch((uchar *)name, i)) < 0)
continue;
/* found; copy out */
n = 0;
while ((len > n++) && (*buf++ = env_get_char(val++)) != '\0')
;
if (len == n)
*buf = '\0';
return (n);
}
return (-1);
}
//--------------------Env_common.c (uboot1.1.16_256m-for36---v1.01\common)------------------------
uchar(*env_get_char)(int)=env_get_char_init;
static uchar env_get_char_init (int index) //从default_environment中取出一个字符
{
uchar c;
/* if crc was bad, use the default environment */
if (gd->env_valid) //在前面的env_init函数中env_valid的值被设为0
{
c = env_get_char_spec(index);
} else {
c = default_environment[index];
}
return (c);
}
//-------------------------Env_nowhere.c (uboot1.1.16_256m-for36---v1.01\common)-------------------
uchar env_get_char_spec 函数
uchar env_get_char_spec (int index)
{
return ( *((uchar *)(gd->env_addr + index)) );
}
⑥接下来调用serial_init
//-----------------------Serial.c (uboot1.1.16_256m-for36---v1.01\cpu\s3c64xx)------------------------
void serial_setbrg(void)
{
DECLARE_GLOBAL_DATA_PTR; //这里我们并没有初始化串口,因为汇编中已 //经初始化过了。
int i;
for (i = 0; i < 100; i++);
}
int serial_init(void)
{
serial_setbrg();
return (0);
}
⑦然后调用console_init_f
//----------------------Console.c (uboot1.1.16_256m-for36---v1.01\common)---------------------------
/* Called before relocation - use serial functions */
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);
}
这里我们设置控制台数量为1,其实当前控制台就是串口,如果禁用了控制台,并且在环境变量数组default environment中找到了silent这样一个环境变量,那么在gd_t的标记成员上,我们应该加上GD_FLG_SILENT标记。
⑧接着调用display_banner,这个函数主要用来打印一些调试信息。
//------------------------------Board.c (uboot1.1.16_256m-for36---v1.01\lib_arm)----------------------
static int display_banner (void)
{
printf ("\n\n%s\n\n", version_string);
debug ("U-Boot code: %08lX -> %08lX BSS: -> %08lX\n",
_armboot_start, _bss_start, _bss_end);
#ifdef CONFIG_MEMORY_UPPER_CODE /* by scsuh */
debug("\t\bMalloc and Stack is above the U-Boot Code.\n");
#else
debug("\t\bMalloc and Stack is below the U-Boot Code.\n");
#endif
#ifdef CONFIG_MODEM_SUPPORT
debug ("Modem Support enabled\n");
#endif
#ifdef CONFIG_USE_IRQ
debug ("IRQ Stack: %08lx\n", IRQ_STACK_START);
debug ("FIQ Stack: %08lx\n", FIQ_STACK_START);
#endif
return (0);
}
//-------------------------Common.h (uboot1.1.16_256m-for36---v1.01\include)--------------------------
#ifdef DEBUG
#define debug(fmt,args...) printf (fmt ,##args)
#define debugX(level,fmt,args...) if (DEBUG>=level) printf(fmt,##args);
#else
#define debug(fmt,args...)
#define debugX(level,fmt,args...)
#endif /* DEBUG */
⑨接下来宏CONFIG_DISPLAY_CPUINFO控制调用print_cpuinfo,主要也是打印uboot版本和关于时钟的一些信息。
//----------------------Speed.c (uboot1.1.16_256m-for36---v1.01\cpu\s3c64xx\s3c6410)--------------
int print_cpuinfo(void)
{
printf("****************************************\r\n");
printf("** u-boot 1.1.6 **\r\n");
printf("** Updated for TE6410 Board **\r\n");
printf("** Version 1.0 (10-01-15) **\r\n");
printf("** OEM: Forlinx Embedded **\r\n");
printf("** Web: http://www.witech.com.cn **\r\n");
printf("****************************************\r\n");
printf("\nCPU: S3C6410 @%dMHz\n", get_ARMCLK()/1000000);
printf(" Fclk = %dMHz, Hclk = %dMHz, Pclk = %dMHz",
get_FCLK()/1000000, get_HCLK()/1000000, get_PCLK()/1000000);
/**************
* Display Serial SRC
***************/
#if defined(CONFIG_CLKSRC_CLKUART)
puts(", Serial = CLKUART ");
#else
puts(", Serial = PCLK ");
#endif
if(OTHERS_REG & 0x80)
printf("(SYNC Mode) \n");
else
printf("(ASYNC Mode) \n");
return 0;
}
⑩然后通过宏CONFIG_DISPLAY_BOARDINFO我们控制调用checkboard,就是打印板的信息。
//--------------Smdk6410.c (uboot1.1.16_256m-for36---v1.01\board\samsung\smdk6410)---------
#ifdef CONFIG_DISPLAY_BOARDINFO
int checkboard(void)
{
printf("Board: SMDK6410\n");
return (0);
}
#endif
⑪接着调用dram_init,它完成了向gd_t结构传递关于内存地址和大小信息的任务
//------------Smdk6410.c (uboot1.1.16_256m-for36---v1.01\board\samsung\smdk6410)-----------
int dram_init(void)
{
DECLARE_GLOBAL_DATA_PTR;
gd->bd->bi_dram[0].start = PHYS_SDRAM_1; //0x50000000
gd->bd->bi_dram[0].size = PHYS_SDRAM_1_SIZE;
//在Smdk6410.h (uboot1.1.16_256m-for36---v1.01\include\configs) 中定义了
//#define PHYS_SDRAM_1_SIZE 0x10000000 //128MB
return 0;
}
⑫最后调用display_dram_config,如果定义了DEBUG,则打印每个bank的信息,否则只打印出整个内存空间的大小。
//---------------------------Board.c (uboot1.1.16_256m-for36---v1.01\lib_arm)----------------------
static int display_dram_config (void)
{
int i;
#ifdef DEBUG
puts ("RAM Configuration:\n");
//Smdk6410.h中定义CONFIG_NR_DRAM_BANKS 的值为1
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); } 到这里我的uboot的初始化序列init_sequence就告一段落了。 4.根据宏CFG_NO_FLASH的配置情况初始化flash_info_t信息结构,并打印flash信息, #ifndef CFG_NO_FLASH /* configure available FLASH banks */ size = flash_init (); display_flash_config (size); #endif /* CFG_NO_FLASH */ //------------------Flash.h (uboot1.1.16_256m-for36---v1.01\include)----------------------------------- typedef struct { ulong size; /* total bank size in bytes */ ushort sector_count; /* number of erase units */ ulong flash_id; /* combined device & manufacturer code */ ulong start[CFG_MAX_FLASH_SECT]; /* physical sector start addresses */ uchar protect[CFG_MAX_FLASH_SECT]; /* sector protection status */ #ifdef CFG_FLASH_CFI uchar portwidth; /* the width of the port */ uchar chipwidth; /* the width of the chip */ ushort buffer_size; /* # of bytes in write buffer */ ulong erase_blk_tout; /* maximum block erase timeout */ ulong write_tout; /* maximum write timeout */ ulong buffer_write_tout; /* maximum buffer write timeout */ ushort vendor; /* the primary vendor id */ ushort cmd_reset; /* vendor specific reset command */ ushort interface; /* used for x8/x16 adjustments */ ushort legacy_unlock; /* support Intel legacy (un)locking */ uchar manufacturer_id; /* manufacturer id */ ushort device_id; /* device id */ ushort device_id2; /* extended device id */ ushort ext_addr; /* extended query table address */ ushort cfi_version; /* cfi version */ #endif } flash_info_t; // //------------------Flash.c (uboot1.1.16_256m-for36---v1.01\board\samsung\smdk6410)-------------- flash_info_t flash_info[CFG_MAX_FLASH_BANKS]; //跟前述一样CFG_MAX_FLASH_BANKS定义在smdk6410.h中,这里值为0 ulong flash_init (void) { int i, j; ulong size = 0; for (i = 0; i < CFG_MAX_FLASH_BANKS; i++) { ulong flashbase = 0; flash_info[i].flash_id = #if defined(CONFIG_AMD_LV400) (AMD_MANUFACT & FLASH_VENDMASK) | (AMD_ID_LV400B & FLASH_TYPEMASK); #elif defined(CONFIG_AMD_LV800) (AMD_MANUFACT & FLASH_VENDMASK) | (AMD_ID_LV800B & FLASH_TYPEMASK); #else #error "Unknown flash configured" #endif flash_info[i].size = FLASH_BANK_SIZE; flash_info[i].sector_count = CFG_MAX_FLASH_SECT; memset (flash_info[i].protect, 0, CFG_MAX_FLASH_SECT);//清除扇区写保护状态 if (i == 0) flashbase = CFG_FLASH_BASE; else panic ("configured too many flash banks!\n"); for (j = 0; j < flash_info[i].sector_count; j++) { if (j <= 3) { /* 1st one is 16 KB */ if (j == 0) { //start成员是各扇区物理起始地址数组 flash_info[i].start[j] = //第一个扇区16KB flashbase + 0; } /* 2nd and 3rd are both 8 KB */ if ((j == 1) || (j == 2)) { //第2、3个扇区大小都是8KB flash_info[i].start[j] = flashbase + 0x4000 + (j - 1) * 0x2000; } /* 4th 32 KB */ if (j == 3) { //第4个扇区大小是32KB flash_info[i].start[j] = flashbase + 0x8000; } } else { flash_info[i].start[j] = //MAIN_SECT_SIZE是主扇区规格,这里是64KB flashbase + (j - 3) * MAIN_SECT_SIZE; } } size += flash_info[i].size; //临时变量size记录了整个flash的空间大小并返回 } //设置需要写保护的区域,如果需要打开或者关闭写保护还会调用flash底层控制的代码 , //并设置flash_info[i].protect的标志位 //flash_protect在Flash.c (uboot1.1.16_256m-for36---v1.01\common)中,其中用于底层实现写 //保护的flash_real_protect在Cfi_flash.c (uboot1.1.16_256m-for36---v1.01\drivers)中,这里与//uboot主框架关系不是很大的底层小编不会全部展开来讲。 flash_protect (FLAG_PROTECT_SET, CFG_FLASH_BASE, CFG_FLASH_BASE + monitor_flash_len - 1, &flash_info[0]); flash_protect (FLAG_PROTECT_SET, CFG_ENV_ADDR, CFG_ENV_ADDR + CFG_ENV_SIZE - 1, &flash_info[0]); return size; } //-----------------------------Board.c (uboot1.1.16_256m-for36---v1.01\lib_arm)------------------------ static void display_flash_config (ulong size) { puts ("Flash: "); print_size (size, "\n"); //打印前面flash_init 计算出的Flash的总大小 } #endif /* CFG_NO_FLASH */ 5.接下来初始化真空荧光显示频的一些信息 #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 */ 6.接下来初始化uboot关于LCD显示屏的信息,这块所做的事情与前面VFD是基本类似的 #ifdef CONFIG_LCD # ifndef PAGE_SIZE //内存的页大小是4KB,这里是用于4K对齐 # 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); //4KB对齐 size = lcd_setmem (addr); gd->fb_base = addr; //获取显示内存首地址并保存在gd_t结构体中 #endif /* CONFIG_LCD */ //----------------------------Lcd.c (uboot1.1.16_256m-for36---v1.01\common)--------------------------- ulong lcd_setmem (ulong addr) { ulong size; int line_length = (panel_info.vl_col * NBITS (panel_info.vl_bpix)) / 8; debug ("LCD panel info: %d x %d, %d bit/pix\n", panel_info.vl_col, panel_info.vl_row, NBITS (panel_info.vl_bpix) ); size = line_length * panel_info.vl_row; /* Round up to nearest full page */ size = (size + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1); //4KB对齐 /* Allocate pages for the frame buffer. */ addr -= size; //显示内存放在bss段的底部 debug ("Reserving %ldk for LCD Framebuffer at: %08lx\n", size>>10, addr); return (addr); } //----------------------------Lcd.h (uboot1.1.16_256m-for36---v1.01\include)----------------------------- #define NBITS(bit_code) (1 << (bit_code)) typedef struct vidinfo { ushort vl_col; /* Number of columns (i.e. 640) */ ushort vl_row; /* Number of rows (i.e. 480) */ ushort vl_width; /* Width of display area in millimeters */ ushort vl_height; /* Height of display area in millimeters */ /* LCD configuration register */ u_char vl_clkp; /* Clock polarity */ u_char vl_oep; /* Output Enable polarity */ u_char vl_hsp; /* Horizontal Sync polarity */ u_char vl_vsp; /* Vertical Sync polarity */ u_char vl_dp; /* Data polarity */ u_char vl_bpix; /* Bits per pixel, 0 = 1, 1 = 2, 2 = 4, 3 = 8 */ u_char vl_lbw; /* LCD Bus width, 0 = 4, 1 = 8 */ u_char vl_splt; /* Split display, 0 = single-scan, 1 = dual-scan */ u_char vl_clor; /* Color, 0 = mono, 1 = color */ u_char vl_tft; /* 0 = passive, 1 = TFT */ /* Horizontal control register. Timing from data sheet */ ushort vl_wbl; /* Wait between lines */ /* Vertical control register */ u_char vl_vpw; /* Vertical sync pulse width */ u_char vl_lcdac; /* LCD AC timing */ u_char vl_wbf; /* Wait between frames */ } vidinfo_t; extern vidinfo_t panel_info; 7.获取堆空间的首末地址,同时可以清除堆空间的数据,初始化堆空间 /* armboot_start is defined in the board-specific linker script */ #ifdef CONFIG_MEMORY_UPPER_CODE /* by scsuh */ mem_malloc_init (CFG_UBOOT_BASE + CFG_UBOOT_SIZE - CFG_MALLOC_LEN - CFG_STACK_SIZE); #else mem_malloc_init (_armboot_start - CFG_MALLOC_LEN); #endif //----------------------------Board.c (uboot1.1.16_256m-for36---v1.01\lib_arm)------------------------- void mem_malloc_init (ulong dest_addr) { mem_malloc_start = dest_addr; mem_malloc_end = dest_addr + CFG_MALLOC_LEN; mem_malloc_brk = mem_malloc_start; /* memset ((void *) mem_malloc_start, 0, mem_malloc_end - mem_malloc_start); */ } 8.nand_init初始化nandflash,这块算是比较复杂的,一些地址的绑定和nand信息的保存 关于nand_chip结构体的概述,参见文档NandFlash驱动.pdf #if defined(CONFIG_SMDK6400) || defined(CONFIG_SMDK6410) || defined(CONFIG_SMDK6430) || defined(CONFIG_SMDK2450) || defined(CONFIG_SMDK2416) #if defined(CONFIG_NAND) puts ("NAND: "); nand_init(); /* go init the NAND */ #endif //----------------------------Nand.h (uboot1.1.16_256m-for36---v1.01\include)-------------------------- typedef struct mtd_info nand_info_t; //-----------------Mtd.h (uboot1.1.16_256m-for36---v1.01\include\linux\mtd)------------------------- struct mtd_info { u_char type; u_int32_t flags; u_int32_t size; // Total size of the MTD /* "Major" erase size for the device. Na茂ve users may take this * to be the only erase size available, or may use the more detailed * information below if they desire */ u_int32_t erasesize; /* Minimal writable flash unit size. In case of NOR flash it is 1 (even * though individual bits can be cleared), in case of NAND flash it is * one NAND page (or half, or one-fourths of it), in case of ECC-ed NOR * it is of ECC block size, etc. It is illegal to have writesize = 0. * Any driver registering a struct mtd_info must ensure a writesize of * 1 or larger. */ u_int32_t writesize; u_int32_t oobsize; // Amount of OOB data per block (e.g. 16) u_int32_t oobavail; // Available OOB bytes per block u_int32_t ecctype; u_int32_t eccsize; /* * Reuse some of the above unused fields in the case of NOR flash * with configurable programming regions to avoid modifying the * user visible structure layout/size. Only valid when the * MTD_PROGRAM_REGIONS flag is set. * (Maybe we should have an union for those?) */ #define MTD_PROGREGION_CTRLMODE_VALID(mtd) (mtd)->oobsize #define MTD_PROGREGION_CTRLMODE_INVALID(mtd) (mtd)->ecctype // Kernel-only stuff starts here. char *name; int index; /* ecc layout structure pointer - read only ! */ struct nand_ecclayout *ecclayout; /* Data for variable erase regions. If numeraseregions is zero, * it means that the whole device has erasesize as given above. */ int numeraseregions; struct mtd_erase_region_info *eraseregions; /* This really shouldn't be here. It can go away in 2.5 */ u_int32_t bank_size; int (*erase) (struct mtd_info *mtd, struct erase_info *instr); /* This stuff for eXecute-In-Place */ int (*point) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char **mtdbuf); /* We probably shouldn't allow XIP if the unpoint isn't a NULL */ void (*unpoint) (struct mtd_info *mtd, u_char * addr, loff_t from, size_t len); int (*read) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); int (*write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf); int (*read_oob) (struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops); int (*write_oob) (struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops); /* * Methods to access the protection register area, present in some * flash devices. The user data is one time programmable but the * factory data is read only. */ int (*get_fact_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len); int (*read_fact_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); int (*get_user_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len); int (*read_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); int (*write_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); int (*lock_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len); /* XXX U-BOOT XXX */ #if 0 /* kvec-based read/write methods. NB: The 'count' parameter is the number of _vectors_, each of which contains an (ofs, len) tuple. */ int (*writev) (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen); #endif /* Sync */ void (*sync) (struct mtd_info *mtd); /* Chip-supported device locking */ int (*lock) (struct mtd_info *mtd, loff_t ofs, size_t len); int (*unlock) (struct mtd_info *mtd, loff_t ofs, size_t len); /* Power Management functions */ int (*suspend) (struct mtd_info *mtd); void (*resume) (struct mtd_info *mtd); /* Bad block management functions */ int (*block_isbad) (struct mtd_info *mtd, loff_t ofs); int (*block_markbad) (struct mtd_info *mtd, loff_t ofs); /* XXX U-BOOT XXX */ #if 0 struct notifier_block reboot_notifier; /* default mode before reboot */ #endif /* ECC status information */ struct mtd_ecc_stats ecc_stats; /* Subpage shift (NAND) */ int subpage_sft; void *priv; struct module *owner; int usecount; /* If the driver is something smart, like UBI, it may need to maintain * its own reference counting. The below functions are only for driver. * The driver may register its callbacks. These callbacks are not * supposed to be called by MTD users */ int (*get_device) (struct mtd_info *mtd); void (*put_device) (struct mtd_info *mtd); }; //-----------------Nand.h (uboot1.1.16_256m-for36---v1.01\include\linux\mtd)------------------------ struct nand_flash_dev { char *name; int id; unsigned long pagesize; unsigned long chipsize; unsigned long erasesize; unsigned long options; }; struct nand_chip { void __iomem *IO_ADDR_R; void __iomem *IO_ADDR_W; uint8_t (*read_byte)(struct mtd_info *mtd); u16 (*read_word)(struct mtd_info *mtd); void (*write_buf)(struct mtd_info *mtd, const uint8_t *buf, int len); void (*read_buf)(struct mtd_info *mtd, uint8_t *buf, int len); int (*verify_buf)(struct mtd_info *mtd, const uint8_t *buf, int len); void (*select_chip)(struct mtd_info *mtd, int chip); int (*block_bad)(struct mtd_info *mtd, loff_t ofs, int getchip); int (*block_markbad)(struct mtd_info *mtd, loff_t ofs); void (*cmd_ctrl)(struct mtd_info *mtd, int dat, unsigned int ctrl); int (*dev_ready)(struct mtd_info *mtd); void (*cmdfunc)(struct mtd_info *mtd, unsigned command, int column, int page_addr); int (*waitfunc)(struct mtd_info *mtd, struct nand_chip *this); void (*erase_cmd)(struct mtd_info *mtd, int page); int (*scan_bbt)(struct mtd_info *mtd); int (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state, int status, int page); int (*write_page)(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *buf, int page, int cached, int raw); int chip_delay; unsigned int options; int page_shift; int phys_erase_shift; int bbt_erase_shift; int chip_shift; int numchips; unsigned long chipsize; int pagemask; int pagebuf; int subpagesize; uint8_t cellinfo; int badblockpos; nand_state_t state; uint8_t *oob_poi; #if 0 struct nand_hw_control *controller; #endif struct nand_ecclayout *ecclayout; struct nand_ecc_ctrl ecc; struct nand_buffers *buffers; /* XXX U-BOOT XXX */ #if 0 struct nand_hw_control hwcontrol; #endif struct mtd_oob_ops ops; uint8_t *bbt; struct nand_bbt_descr *bbt_td; struct nand_bbt_descr *bbt_md; struct nand_bbt_descr *badblock_pattern; void *priv; }; //---------------------Nand.c (uboot1.1.16_256m-for36---v1.01\drivers\nand)-------------------------- static ulong base_address[CFG_MAX_NAND_DEVICE] = CFG_NAND_BASE_LIST; //base_address是各个nand控制器的读写寄存器地址序列,这里我们只用一个nand,所以 //CFG_MAX_NAND_DEVICE值为1,CFG_NAND_BASE_LIST的值被定义为 //CFG_NAND_BASE,而它的值为0x70200010 static struct nand_chip nand_chip[CFG_MAX_NAND_DEVICE]; nand_info_t nand_info[CFG_MAX_NAND_DEVICE]; void nand_init(void) { int i; unsigned int size = 0; for (i = 0; i < CFG_MAX_NAND_DEVICE; i++) { //CFG_MAX_NAND_DEVICE的值是1 nand_init_chip(&nand_info[i], &nand_chip[i], base_address[i]); size += nand_info[i].size; //得到nand整个空间的大小 if (nand_curr_device == -1) nand_curr_device = i; } printf("%lu MB ", size / (1024 * 1024)); //打印nand大小的信息 #if defined(CFG_NAND_FLASH_BBT) printf("(Flash Based BBT Enabled)"); #endif printf("\n"); #ifdef CFG_NAND_SELECT_DEVICE /* * Select the chip in the board/cpu specific driver */ //board_nand_select_device这个函数在Uboot中没有实现 board_nand_select_device(nand_info[nand_curr_device].priv, nand_curr_device); #endif } static void nand_init_chip(struct mtd_info *mtd, struct nand_chip *nand, ulong base_addr) { mtd->priv = nand; //绑定nand_chip到nand_info上去 //绑定nand控制器的读写寄存器地址 nand->IO_ADDR_R = nand->IO_ADDR_W = (void __iomem *)base_addr; //默认地址 board_nand_init(nand); //printf("nand_init_chip!\n"); if (nand_scan(mtd, 1) == 0) { if (!mtd->name) mtd->name = (char *)default_nand_name; } else mtd->name = NULL; } //----------------------Nand.h (uboot1.1.16_256m-for36---v1.01\include\linux\mtd)--------------------- struct nand_flash_dev { char *name; int id; unsigned long pagesize; unsigned long chipsize; unsigned long erasesize; unsigned long options; }; //---------------------Nand_ids.c (uboot1.1.16_256m-for36---v1.01\drivers\nand)--------------------- struct nand_flash_dev nand_flash_ids[] = { {"NAND 1MiB 5V 8-bit", 0x6e, 256, 1, 0x1000, 0}, {"NAND 2MiB 5V 8-bit", 0x64, 256, 2, 0x1000, 0}, {"NAND 4MiB 5V 8-bit", 0x6b, 512, 4, 0x2000, 0}, {"NAND 1MiB 3,3V 8-bit", 0xe8, 256, 1, 0x1000, 0}, {"NAND 1MiB 3,3V 8-bit", 0xec, 256, 1, 0x1000, 0}, {"NAND 2MiB 3,3V 8-bit", 0xea, 256, 2, 0x1000, 0}, /* {"NAND 4MiB 3,3V 8-bit", 0xd5, 512, 4, 0x2000, 0}, */ {"NAND 4MiB 3,3V 8-bit", 0xe3, 512, 4, 0x2000, 0}, {"NAND 4MiB 3,3V 8-bit", 0xe5, 512, 4, 0x2000, 0}, {"NAND 8MiB 3,3V 8-bit", 0xd6, 512, 8, 0x2000, 0}, {"NAND 8MiB 1,8V 8-bit", 0x39, 512, 8, 0x2000, 0}, {"NAND 8MiB 3,3V 8-bit", 0xe6, 512, 8, 0x2000, 0}, {"NAND 8MiB 1,8V 16-bit", 0x49, 512, 8, 0x2000, NAND_BUSWIDTH_16}, {"NAND 8MiB 3,3V 16-bit", 0x59, 512, 8, 0x2000, NAND_BUSWIDTH_16}, {"NAND 16MiB 1,8V 8-bit", 0x33, 512, 16, 0x4000, 0}, {"NAND 16MiB 3,3V 8-bit", 0x73, 512, 16, 0x4000, 0}, {"NAND 16MiB 1,8V 16-bit", 0x43, 512, 16, 0x4000, NAND_BUSWIDTH_16}, {"NAND 16MiB 3,3V 16-bit", 0x53, 512, 16, 0x4000, NAND_BUSWIDTH_16}, {"NAND 32MiB 1,8V 8-bit", 0x35, 512, 32, 0x4000, 0}, {"NAND 32MiB 3,3V 8-bit", 0x75, 512, 32, 0x4000, 0}, {"NAND 32MiB 1,8V 16-bit", 0x45, 512, 32, 0x4000, NAND_BUSWIDTH_16}, {"NAND 32MiB 3,3V 16-bit", 0x55, 512, 32, 0x4000, NAND_BUSWIDTH_16}, {"NAND 64MiB 1,8V 8-bit", 0x36, 512, 64, 0x4000, 0}, {"NAND 64MiB 3,3V 8-bit", 0x76, 512, 64, 0x4000, 0}, {"NAND 64MiB 1,8V 16-bit", 0x46, 512, 64, 0x4000, NAND_BUSWIDTH_16}, {"NAND 64MiB 3,3V 16-bit", 0x56, 512, 64, 0x4000, NAND_BUSWIDTH_16}, {"NAND 128MiB 1,8V 8-bit", 0x78, 512, 128, 0x4000, 0}, {"NAND 128MiB 1,8V 8-bit", 0x39, 512, 128, 0x4000, 0}, {"NAND 128MiB 3,3V 8-bit", 0x79, 512, 128, 0x4000, 0}, {"NAND 128MiB 1,8V 16-bit", 0x72, 512, 128, 0x4000, NAND_BUSWIDTH_16}, {"NAND 128MiB 1,8V 16-bit", 0x49, 512, 128, 0x4000, NAND_BUSWIDTH_16}, {"NAND 128MiB 3,3V 16-bit", 0x74, 512, 128, 0x4000, NAND_BUSWIDTH_16}, {"NAND 128MiB 3,3V 16-bit", 0x59, 512, 128, 0x4000, NAND_BUSWIDTH_16}, {"NAND 256MiB 3,3V 8-bit", 0x71, 512, 256, 0x4000, 0}, //jkeqiang modify it {"NAND 256MiB 3,3V 8-bit", 0xda, 0, 256, 0x20000, 0}, // {"NAND 256MiB 3,3V 8-bit", 0xda, 2048, 256, 0x20000, 0}, /* * These are the new chips with large page size. The pagesize and the * erasesize is determined from the extended id bytes */ #define LP_OPTIONS (NAND_SAMSUNG_LP_OPTIONS | NAND_NO_READRDY | NAND_NO_AUTOINCR) #define LP_OPTIONS16 (LP_OPTIONS | NAND_BUSWIDTH_16) /*512 Megabit */ {"NAND 64MiB 1,8V 8-bit", 0xA2, 0, 64, 0, LP_OPTIONS}, {"NAND 64MiB 3,3V 8-bit", 0xF2, 0, 64, 0, LP_OPTIONS}, {"NAND 64MiB 1,8V 16-bit", 0xB2, 0, 64, 0, LP_OPTIONS16}, {"NAND 64MiB 3,3V 16-bit", 0xC2, 0, 64, 0, LP_OPTIONS16}, /* 1 Gigabit */ {"NAND 128MiB 1,8V 8-bit", 0xA1, 0, 128, 0, LP_OPTIONS}, {"NAND 128MiB 3,3V 8-bit", 0xF1, 0, 128, 0, LP_OPTIONS}, {"NAND 128MiB 1,8V 16-bit", 0xB1, 0, 128, 0, LP_OPTIONS16}, {"NAND 128MiB 3,3V 16-bit", 0xC1, 0, 128, 0, LP_OPTIONS16}, /* 2 Gigabit */ {"NAND 256MiB 1,8V 8-bit", 0xAA, 0, 256, 0, LP_OPTIONS}, {"NAND 256MiB 3,3V 8-bit", 0xDA, 0, 256, 0, LP_OPTIONS}, {"NAND 256MiB 1,8V 16-bit", 0xBA, 0, 256, 0, LP_OPTIONS16}, {"NAND 256MiB 3,3V 16-bit", 0xCA, 0, 256, 0, LP_OPTIONS16}, /* 4 Gigabit */ {"NAND 512MiB 1,8V 8-bit", 0xAC, 0, 512, 0, LP_OPTIONS}, {"NAND 512MiB 3,3V 8-bit", 0xDC, 0, 512, 0, LP_OPTIONS}, {"NAND 512MiB 1,8V 16-bit", 0xBC, 0, 512, 0, LP_OPTIONS16}, {"NAND 512MiB 3,3V 16-bit", 0xCC, 0, 512, 0, LP_OPTIONS16}, /* 8 Gigabit */ {"NAND 1GiB 1,8V 8-bit", 0xA3, 0, 1024, 0, LP_OPTIONS}, {"NAND 1GiB 3,3V 8-bit", 0xD3, 0, 1024, 0, LP_OPTIONS}, {"NAND 1GiB 1,8V 16-bit", 0xB3, 0, 1024, 0, LP_OPTIONS16}, {"NAND 1GiB 3,3V 16-bit", 0xC3, 0, 1024, 0, LP_OPTIONS16}, /* 16 Gigabit */ {"NAND 2GiB 1,8V 8-bit", 0xA5, 0, 2048, 0, LP_OPTIONS}, {"NAND 2GiB 3,3V 8-bit", 0xD5, 4096, 2048, 512*1024, LP_OPTIONS}, {"NAND 2GiB 1,8V 16-bit", 0xB5, 0, 2048, 0, LP_OPTIONS16}, {"NAND 2GiB 3,3V 16-bit", 0xC5, 0, 2048, 0, LP_OPTIONS16}, /* * Renesas AND 1 Gigabit. Those chips do not support extended id and * have a strange page/block layout ! The chosen minimum erasesize is * 4 * 2 * 2048 = 16384 Byte, as those chips have an array of 4 page * planes 1 block = 2 pages, but due to plane arrangement the blocks * 0-3 consists of page 0 + 4,1 + 5, 2 + 6, 3 + 7 Anyway JFFS2 would * increase the eraseblock size so we chose a combined one which can be * erased in one go There are more speed improvements for reads and * writes possible, but not implemented now */ {"AND 128MiB 3,3V 8-bit", 0x01, 2048, 128, 0x4000, NAND_IS_AND | NAND_NO_AUTOINCR |NAND_NO_READRDY | NAND_4PAGE_ARRAY | BBT_AUTO_REFRESH }, {NULL,} }; //-----------------------Nand.c (uboot1.1.16_256m-for36---v1.01\cpu\s3c64xx)-------------------------- /* * Function for checking device ready pin * Written by jsgood */ static int s3c_nand_device_ready(struct mtd_info *mtdinfo) { while (!(readl(NFSTAT) & NFSTAT_RnB)) {} //FLASH忙检测 return 1; } void board_nand_init(struct nand_chip *nand) { #if defined(CFG_NAND_HWECC) int i; u_char tmp; u_char dev_id; struct nand_flash_dev *type = NULL; #endif if (NFCONF_REG & 0x80000000) boot_nand = 1; else boot_nand = 0; NFCONT_REG &= ~NFCONT_WP; //打开写保护 nand->IO_ADDR_R = (void __iomem *)(NFDATA);//指定NFDATA为读写寄存器 nand->IO_ADDR_W = (void __iomem *)(NFDATA); //绑定Nand.c (uboot1.1.16_256m-for36---v1.01\cpu\s3c64xx)中与寄存器操作直接相关的函数 nand->cmd_ctrl = s3c_nand_hwcontrol; //nand的发送控制函数(地址、命令等) nand->dev_ready = s3c_nand_device_ready;//nand的忙检测函数 nand->scan_bbt = s3c_nand_scan_bbt;//nand的坏块检查及检查模型的确定的函数 nand->options = 0; /*bbt指向一块在nand_default_bbt函数中分配的内存,若options中没有定义NAND_USE_FLASH_BBT,MTD就直接在bbt指向的内存中建立bbt,否则就会先从NAND芯片中查找bbt是否存在,若存在,就把bbt的内容读出来并保存到bbt指向的内存中,若不存在,则在bbt指向的内存中建立bbt,最后把它写入到NAND芯片中去。 */ #if defined(CFG_NAND_FLASH_BBT) //根据宏我们配置nand的可选项 nand->options |= NAND_USE_FLASH_BBT; #else nand->options |= NAND_SKIP_BBTSCAN; //这个宏表示跳过扫描bbt坏块 #endif #if defined(CFG_NAND_HWECC) //根据宏配置硬件ECC或者软件ECC校验 nand->ecc.mode = NAND_ECC_HW; //模式标志 nand->ecc.hwctl = s3c_nand_enable_hwecc; //绑定到硬件ECC使能函数 nand->ecc.calculate = s3c_nand_calculate_ecc; //绑定硬件ECC提取函数 nand->ecc.correct = s3c_nand_correct_data; //绑定底层错误数据校正函数 //上述函数在Nand.c (uboot1.1.16_256m-for36---v1.01\cpu\s3c64xx)中定义 s3c_nand_hwcontrol(0, NAND_CMD_READID, NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); s3c_nand_hwcontrol(0, 0x00, NAND_CTRL_CHANGE | NAND_NCE | NAND_ALE); s3c_nand_hwcontrol(0, 0x00, NAND_NCE | NAND_ALE); s3c_nand_hwcontrol(0, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); //这块寄存器操作似乎有点问题,还没有完全理解 s3c_nand_device_ready(0); //等待忙状态结束 tmp = readb(nand->IO_ADDR_R); /* Maf. ID */ dev_id = tmp = readb(nand->IO_ADDR_R); /* Device ID */ for (i = 0; nand_flash_ids[i].name != NULL; i++) {//查找匹配的flash信息,用于获取页大小 if (tmp == nand_flash_ids[i].id) { type = &nand_flash_ids[i]; break; } } nand->cellinfo = readb(nand->IO_ADDR_R); /* 3rd byte */ //读取flash的详细信息 tmp = readb(nand->IO_ADDR_R); /* 4th byte */ if (!type->pagesize) { //如果匹配的flash信息中没有页大小信息 if (((nand->cellinfo >> 2) & 0x3) == 0) { //识别晶元级别,这里是2层晶元 nand_type = S3C_NAND_TYPE_MLC; nand->options |= NAND_NO_SUBPAGE_WRITE; /* NOP = 1 if MLC */ nand->ecc.read_page = s3c_nand_read_page_4bit; //设置ECC的一些相关信 //息和特殊页的读写函数, //以及ECC的分布方式 nand->ecc.write_page = s3c_nand_write_page_4bit; nand->ecc.size = 512; //OOB区的大小 nand->ecc.bytes = 8; /* really 7 bytes */ if ((1024 << (tmp & 0x3)) > 512) { nand->ecc.layout = &s3c_nand_oob_mlc_64; } else { nand->ecc.layout = &s3c_nand_oob_16; } } else { nand_type = S3C_NAND_TYPE_MLC; nand->options |= NAND_NO_SUBPAGE_WRITE; /* NOP = 1 if MLC */ nand->ecc.read_page = s3c_nand_read_page_4bit; nand->ecc.write_page = s3c_nand_write_page_4bit; nand->ecc.size = 512; nand->ecc.bytes = 8; /* really 7 bytes */ nand->ecc.layout = &s3c_nand_oob_mlc_64; //jkeqiang if((1024 << (tmp & 0x3)) > 2048) { printf("select s3c_nand_oob_mlc_128\n"); nand->ecc.layout = &s3c_nand_oob_mlc_128; } } } else { nand_type = S3C_NAND_TYPE_MLC; nand->options |= NAND_NO_SUBPAGE_WRITE; /* NOP = 1 if MLC */ nand->ecc.read_page = s3c_nand_read_page_4bit; nand->ecc.write_page = s3c_nand_write_page_4bit; nand->ecc.size = 512; nand->ecc.bytes = 8; /* really 7 bytes */ nand->ecc.layout = &s3c_nand_oob_mlc_64; //jkeqiang // if((1024 << (tmp & 0x3)) > 2048) if(dev_id == 0xd5) { printf("select s3c_nand_oob_mlc_128\n"); nand->ecc.layout = &s3c_nand_oob_mlc_128; nand->ecc.read_page = s3c_nand_read_page_8bit; nand->ecc.write_page = s3c_nand_write_page_8bit; nand->ecc.size = 512; nand->ecc.bytes = 13; /* really 7 bytes */ nand->ecc.layout = &s3c_nand_oob_mlc_128_8bit; } else { printf("select s3c_nand_oob_mlc_64\n"); } } #else nand->ecc.mode = NAND_ECC_SOFT; //不是硬件ECC就自然是软件ECC了 #endif } 关于这个函数中s3c_nand_hwcontrol、s3c_nand_device_ready两个函数都比较简单我们不展开来讲,而后面在操作系统中我们再着重分析一下s3c_nand_scan_bbt坏块检查,因为这部分代码其实是直接从linux块设备驱动中搬过来的,比较复杂。注意了,在这些传递给函数指针的函数当中,最底层的函数有s3c_nand_hwcontrol、s3c_nand_device_ready、s3c_nand_enable_hwecc、s3c_nand_calculate_ecc、s3c_nand_correct_data,以上这些函数是直接跟片上寄存器打交道的,因此这些被赋值的函数指针,在board_nand_init被调用之后就能被正常使用了,但是对于s3c_nand_read_page_4bit、s3c_nand_write_page_4bit、s3c_nand_read_page_8bit、s3c_nand_write_page_8bit等函数,它们都调用了一些还没有被初始化的函数指针,这些函数指针是在nand_init_chip->nand_scan->nand_scan_ident中通过调用nand_set_defaults和nand_get_flash_type函数进行赋值的,它们的功能最终还是由之前那些最底层的函数来实现。接下来我们将回到nand_init_chip中继续执行nand_scan函数。 //------------------------Nand_base.c (uboot1.1.16_256m-for36---v1.01\drivers\nand)------------------- /** * nand_scan - [NAND Interface] Scan for the NAND device * @mtd: MTD device structure * @maxchips: Number of chips to scan for * * This fills out all the uninitialized function pointers * with the defaults. * The flash ID is read and the mtd/chip structures are * filled with the appropriate values. * The mtd->owner field must be set to the module of the caller * */ int nand_scan(struct mtd_info *mtd, int maxchips) { int ret; #if 0 /* Many callers got this wrong, so check for it for a while... */ if (!mtd->owner && caller_is_module()) { printk(KERN_CRIT "nand_scan() called with NULL mtd->owner!\n"); BUG(); } #endif ret = nand_scan_ident(mtd, maxchips); if (!ret) ret = nand_scan_tail(mtd); return ret; } /** * nand_scan_ident - [NAND Interface] Scan for the NAND device * @mtd: MTD device structure * @maxchips: Number of chips to scan for * * This is the first phase of the normal nand_scan() function. It * reads the flash ID and sets up MTD fields accordingly. * * The mtd->owner field must be set to the module of the caller. */ int nand_scan_ident(struct mtd_info *mtd, int maxchips) { int i, busw, nand_maf_id; struct nand_chip *chip = mtd->priv; struct nand_flash_dev *type; /* Get buswidth to select the correct functions */ busw = chip->options & NAND_BUSWIDTH_16; //设置校正功能的总线位宽 /* Set the default functions */ nand_set_defaults(chip, busw); /* Read the flash type */ type = nand_get_flash_type(mtd, chip, busw, &nand_maf_id); if (!type) { //没有匹配的flash设备ID即打印错误信息 printk (KERN_WARNING "No NAND device found!!!\n"); chip->select_chip(mtd, -1); return 1; } /* Check for a chip array */ for (i = 1; i < maxchips; i++) { //这里我们主要是核查一共外挂了多少片这样的nand, //但是,由于这里是从linux内核抽取的程序,这部分 //功能并不完整,所以实际上这里并没有起什么用 chip->select_chip(mtd, i); /* Send the command for reading device ID */ chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); /* Read manufacturer and device IDs */ if (nand_maf_id != chip->read_byte(mtd) || type->id != chip->read_byte(mtd)) break; } if (i > 1) printk(KERN_INFO "%d NAND chips detected\n", i); /* Store the number of chips and calc total size for mtd */ chip->numchips = i; mtd->size = i * chip->chipsize; return 0; } //-------------------------Nand_base.c (uboot1.1.16_256m-for36---v1.01\drivers\nand)------------------ /* * Set default functions //绑定默认的底层函数,实现基本的驱动层功能,如读写等 */ static void nand_set_defaults(struct nand_chip *chip, int busw) { /* check for proper chip_delay setup, set 20us if not */ if (!chip->chip_delay) chip->chip_delay = 20; /* check, if a user supplied command function given */ if (chip->cmdfunc == NULL) chip->cmdfunc = nand_command; /* check, if a user supplied wait function given */ if (chip->waitfunc == NULL) chip->waitfunc = nand_wait; if (!chip->select_chip) chip->select_chip = nand_select_chip; if (!chip->read_byte) chip->read_byte = busw ? nand_read_byte16 : nand_read_byte; if (!chip->read_word) chip->read_word = nand_read_word; if (!chip->block_bad) chip->block_bad = nand_block_bad; if (!chip->block_markbad) chip->block_markbad = nand_default_block_markbad; if (!chip->write_buf) chip->write_buf = busw ? nand_write_buf16 : nand_write_buf; if (!chip->read_buf) chip->read_buf = busw ? nand_read_buf16 : nand_read_buf; if (!chip->verify_buf) chip->verify_buf = busw ? nand_verify_buf16 : nand_verify_buf; if (!chip->scan_bbt) chip->scan_bbt = nand_default_bbt; #if 0 if (!chip->controller) { chip->controller = &chip->hwcontrol; spin_lock_init(&chip->controller->lock); init_waitqueue_head(&chip->controller->wq); } #endif } //-------------------------Nand_base.c (uboot1.1.16_256m-for36---v1.01\drivers\nand)------------------ /* * Get the flash and manufacturer id and lookup if the type is supported */ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, struct nand_chip *chip, int busw, int *maf_id) { struct nand_flash_dev *type = NULL; int i, dev_id, maf_idx; /* Select the device */ chip->select_chip(mtd, 0); //片选 /* Send the command for reading device ID */ chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); //发送读ID命令 /* Read manufacturer and device IDs */ *maf_id = chip->read_byte(mtd); dev_id = chip->read_byte(mtd); //printf("maf_id=%x,dev_id=%x\n",*maf_id,dev_id); /* Lookup the flash id */ for (i = 0; nand_flash_ids[i].name != NULL; i++) { //根据读到的ID匹配flash型号 if (dev_id == nand_flash_ids[i].id) { type = &nand_flash_ids[i]; break; } } if (!type) //如果没有找到直接返回0 return 0; if (!mtd->name) mtd->name = type->name; //将匹配到的flash型号名直接赋给nand_info chip->chipsize = type->chipsize << 20; //chipsize的单位是MB,这里是2GB /* Newer devices have all the information in additional id bytes */ if (!type->pagesize) { //如果flash序列中没有提供flash相关的信息 //我们通过读ID命令获取相关的信息 int extid; /* The 3rd id byte holds MLC / multichip data */ chip->cellinfo = chip->read_byte(mtd); /* The 4th id byte is the important one */ extid = chip->read_byte(mtd); /* Calc pagesize */ //将计算出来的flash信息保存到nand_info mtd->writesize = 1024 << (extid & 0x3); extid >>= 2; /* Calc oobsize */ mtd->oobsize = (8 << (extid & 0x01)) * (mtd->writesize >> 9); extid >>= 2; /* Calc blocksize. Blocksize is multiples of 64KiB */ mtd->erasesize = (64 * 1024) << (extid & 0x03); extid >>= 2; /* Get buswidth information */ busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0; } else { /* * Old devices have chip data hardcoded in the device id table */ mtd->erasesize = type->erasesize; //将flash序列中的信息保存到nand_info mtd->writesize = type->pagesize; mtd->oobsize = mtd->writesize / 32; busw = type->options & NAND_BUSWIDTH_16; } /* Try to identify manufacturer */ //识别供应商信息 for (maf_idx = 0; nand_manuf_ids[maf_idx].id != 0x0; maf_idx++) { if (nand_manuf_ids[maf_idx].id == *maf_id) break; } /* * Check, if buswidth is correct. Hardware drivers should set * chip correct ! */ if (busw != (chip->options & NAND_BUSWIDTH_16)) { //打印信息 printk(KERN_INFO "NAND device: Manufacturer ID:" " 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id, dev_id, nand_manuf_ids[maf_idx].name, mtd->name); printk(KERN_WARNING "NAND bus width %d instead %d bit\n", (chip->options & NAND_BUSWIDTH_16) ? 16 : 8, busw ? 16 : 8); /* org: return ERR_PTR(-EINVAL) */ return 0; } /* Calculate the address shift from the page size */ chip->page_shift = ffs(mtd->writesize) - 1; //设置nand_chip的一些信息并配置 //options成员的属性 /* Convert chipsize to number of pages per chip -1. */ chip->pagemask = (chip->chipsize >> chip->page_shift) - 1; chip->bbt_erase_shift = chip->phys_erase_shift = ffs(mtd->erasesize) - 1; chip->chip_shift = ffs(chip->chipsize) - 1; /* Set the bad block position */ chip->badblockpos = mtd->writesize > 512 ? NAND_LARGE_BADBLOCK_POS : NAND_SMALL_BADBLOCK_POS; /* Get chip options, preserve non chip based options */ chip->options &= ~NAND_CHIPOPTIONS_MSK; chip->options |= type->options & NAND_CHIPOPTIONS_MSK; /* * Set chip as a default. Board drivers can override it, if necessary */ chip->options |= NAND_NO_AUTOINCR; /* Check if chip is a not a samsung device. Do not clear the * options for chips which are not having an extended id. */ if (*maf_id != NAND_MFR_SAMSUNG && !type->pagesize) chip->options &= ~NAND_SAMSUNG_LP_OPTIONS; /* Check for AND chips with 4 page planes */ if (chip->options & NAND_4PAGE_ARRAY) chip->erase_cmd = multi_erase_cmd; else chip->erase_cmd = single_erase_cmd; /* Do not replace user supplied command function ! */ if (mtd->writesize > 512 && chip->cmdfunc == nand_command) chip->cmdfunc = nand_command_lp; return type; } //--------------------Nand_base.c (uboot1.1.16_256m-for36---v1.01\drivers\nand)----------------------- /** * nand_scan_tail - [NAND Interface] Scan for the NAND device * @mtd: MTD device structure * @maxchips: Number of chips to scan for * * This is the second phase of the normal nand_scan() function. It * fills out all the uninitialized function pointers with the defaults * and scans for a bad block table if appropriate. */ int nand_scan_tail(struct mtd_info *mtd) { int i; struct nand_chip *chip = mtd->priv; if (!(chip->options & NAND_OWN_BUFFERS)) chip->buffers = kmalloc(sizeof(*chip->buffers), GFP_KERNEL); if (!chip->buffers) return -ENOMEM; /* Set the internal oob buffer location, just after the page data */ //设置oob缓冲区位于正常页后 chip->oob_poi = chip->buffers->databuf + mtd->writesize; /* * If no default placement scheme is given, select an appropriate one */ if (!chip->ecc.layout) { //如果ecc分布方式还没有设置,则根据ecc区大小, //选择一种适合的分布方式 switch (mtd->oobsize) {//根据nand_get_flash_type中得到的值选择不同的OOB分布 case 8: chip->ecc.layout = &nand_oob_8; break; case 16: chip->ecc.layout = &nand_oob_16; break; case 64: chip->ecc.layout = &nand_oob_64; break; case 128: chip->ecc.layout = &nand_oob_128; break; default: printk(KERN_WARNING "No oob scheme defined for " "oobsize %d\n", mtd->oobsize); //打印信息 BUG(); } } if (!chip->write_page) chip->write_page = nand_write_page; //链接页写函数 /* * check ECC mode, default to software if 3byte/512byte hardware ECC is * selected and we have 256 byte pagesize fallback to software ECC */ if (!chip->ecc.read_page_raw) chip->ecc.read_page_raw = nand_read_page_raw; if (!chip->ecc.write_page_raw) chip->ecc.write_page_raw = nand_write_page_raw; switch (chip->ecc.mode) { case NAND_ECC_HW: //链接ECC操作和OOB操作的函数 /* Use standard hwecc read page function ? */ if (!chip->ecc.read_page) chip->ecc.read_page = nand_read_page_hwecc; if (!chip->ecc.write_page) chip->ecc.write_page = nand_write_page_hwecc; if (!chip->ecc.read_oob) chip->ecc.read_oob = nand_read_oob_std; if (!chip->ecc.write_oob) chip->ecc.write_oob = nand_write_oob_std; case NAND_ECC_HW_SYNDROME: if (!chip->ecc.calculate || !chip->ecc.correct || !chip->ecc.hwctl) { printk(KERN_WARNING "No ECC functions supplied, " "Hardware ECC not possible\n"); BUG(); } /* Use standard syndrome read/write page function ? */ if (!chip->ecc.read_page) chip->ecc.read_page = nand_read_page_syndrome; if (!chip->ecc.write_page) chip->ecc.write_page = nand_write_page_syndrome; if (!chip->ecc.read_oob) chip->ecc.read_oob = nand_read_oob_syndrome; if (!chip->ecc.write_oob) chip->ecc.write_oob = nand_write_oob_syndrome; if (mtd->writesize >= chip->ecc.size) break; printk(KERN_WARNING "%d byte HW ECC not possible on " "%d byte page size, fallback to SW ECC\n", chip->ecc.size, mtd->writesize); chip->ecc.mode = NAND_ECC_SOFT; case NAND_ECC_SOFT: //软件ECC chip->ecc.calculate = nand_calculate_ecc; chip->ecc.correct = nand_correct_data; chip->ecc.read_page = nand_read_page_swecc; chip->ecc.write_page = nand_write_page_swecc; chip->ecc.read_oob = nand_read_oob_std; chip->ecc.write_oob = nand_write_oob_std; chip->ecc.size = 256; chip->ecc.bytes = 3; break; case NAND_ECC_NONE: printk(KERN_WARNING "NAND_ECC_NONE selected by board driver. " "This is not recommended !!\n"); chip->ecc.read_page = nand_read_page_raw; chip->ecc.write_page = nand_write_page_raw; chip->ecc.read_oob = nand_read_oob_std; chip->ecc.write_oob = nand_write_oob_std; chip->ecc.size = mtd->writesize; chip->ecc.bytes = 0; break; default: printk(KERN_WARNING "Invalid NAND_ECC_MODE %d\n", chip->ecc.mode); BUG(); } /* * The number of bytes available for a client to place data into * the out of band area */ chip->ecc.layout->oobavail = 0; for (i = 0; chip->ecc.layout->oobfree[i].length; i++) chip->ecc.layout->oobavail += chip->ecc.layout->oobfree[i].length; /* * Set the number of read / write steps for one page depending on ECC * mode */ chip->ecc.steps = mtd->writesize / chip->ecc.size; if(chip->ecc.steps * chip->ecc.size != mtd->writesize) { printk(KERN_WARNING "Invalid ecc parameters\n"); BUG(); } chip->ecc.total = chip->ecc.steps * chip->ecc.bytes; /* * Allow subpage writes up to ecc.steps. Not possible for MLC * FLASH. */ if (!(chip->options & NAND_NO_SUBPAGE_WRITE) && !(chip->cellinfo & NAND_CI_CELLTYPE_MSK)) { switch(chip->ecc.steps) { case 2: mtd->subpage_sft = 1; break; case 4: case 8: mtd->subpage_sft = 2; break; } } chip->subpagesize = mtd->writesize >> mtd->subpage_sft; /* XXX U-BOOT XXX */ #if 0 /* Initialize state */ chip->state = FL_READY; #endif /* De-select the device */ chip->select_chip(mtd, -1); /* Invalidate the pagebuffer reference */ chip->pagebuf = -1; /* Fill in remaining MTD driver data */ mtd->type = MTD_NANDFLASH; mtd->flags = MTD_CAP_NANDFLASH; mtd->ecctype = MTD_ECC_SW; mtd->erase = nand_erase; mtd->point = NULL; mtd->unpoint = NULL; mtd->read = nand_read; mtd->write = nand_write; mtd->read_oob = nand_read_oob; mtd->write_oob = nand_write_oob; mtd->sync = nand_sync; mtd->lock = NULL; mtd->unlock = NULL; mtd->suspend = nand_suspend; mtd->resume = nand_resume; mtd->block_isbad = nand_block_isbad; mtd->block_markbad = nand_block_markbad; /* propagate ecc.layout to mtd_info */ mtd->ecclayout = chip->ecc.layout; /* Check, if we should skip the bad block table scan */ if (chip->options & NAND_SKIP_BBTSCAN) return 0; /* Build bad block table */ return chip->scan_bbt(mtd); } //--------------------Nand_base.c (uboot1.1.16_256m-for36---v1.01\drivers\nand)---------------------- static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *buf, int page, int cached, int raw) { int status; chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page); /* jsgood: org: #else block only */ #if defined(CONFIG_NAND_BL1_8BIT_ECC) && (defined(CONFIG_S3C6410) || defined(CONFIG_S3C6430) || defined(CONFIG_S3C2450) || defined(CONFIG_S3C2416)) memset(chip->oob_poi, 0xff, mtd->oobsize); if (page < 16) { s3c_nand_write_page_8bit(mtd, chip, buf); } else { if (unlikely(raw)) //调用的函数也是在nand_scan_tail中初始化 chip->ecc.write_page_raw(mtd, chip, buf); else chip->ecc.write_page(mtd, chip, buf); } #else if (unlikely(raw)) chip->ecc.write_page_raw(mtd, chip, buf); else chip->ecc.write_page(mtd, chip, buf); #endif /* * Cached progamming disabled for now, Not sure if its worth the * trouble. The speed gain is not very impressive. (2.3->2.6Mib/s) */ cached = 0; if (!cached || !(chip->options & NAND_CACHEPRG)) { chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); status = chip->waitfunc(mtd, chip); /* * See if operation failed and additional status checks are * available */ if ((status & NAND_STATUS_FAIL) && (chip->errstat)) status = chip->errstat(mtd, chip, FL_WRITING, status, page); if (status & NAND_STATUS_FAIL) return -EIO; } else { chip->cmdfunc(mtd, NAND_CMD_CACHEDPROG, -1, -1); status = chip->waitfunc(mtd, chip); } #ifdef CONFIG_MTD_NAND_VERIFY_WRITE /* Send command to read back the data */ chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page); if (chip->verify_buf(mtd, buf, mtd->writesize)) return -EIO; #endif return 0; } 到这里我们整个nandflash部分就初始化完了,由于这里是直接利用了linux内核中的驱动部分代码来初始化nand的,所以相对比较复杂了【返回】。 9.onenand_init初始化,这块同样是比较复杂的,与前述的nand初始化架构极其相似,也是从linux内核的块设备驱动程序中抽取出来的,主要是一些地址的绑定和onenand_init信息的保存。 #if defined(CONFIG_ONENAND) puts ("OneNAND: "); onenand_init(); /* go init the One-NAND */ #endif //-----------------------S3c_onenand.h (uboot1.1.16_256m-for36---v1.01\include)----------------------- typedef struct mtd_info onenand_info_t; //-----------------S3c_onenand.h (uboot1.1.16_256m-for36---v1.01\include\linux\mtd)---------------- /** * struct onenand_chip - OneNAND Private Flash Chip Data * @base: [BOARDSPECIFIC] address to access OneNAND * @chipsize: [INTERN] the size of one chip for multichip arrays * @device_id: [INTERN] device ID * @density_mask: chip density, used for DDP devices * @verstion_id: [INTERN] version ID * @options: [BOARDSPECIFIC] various chip options. They can * partly be set to inform onenand_scan about * @erase_shift: [INTERN] number of address bits in a block * @page_shift: [INTERN] number of address bits in a page * @ppb_shift: [INTERN] number of address bits in a pages per block * @page_mask: [INTERN] a page per block mask * @bufferram_index: [INTERN] BufferRAM index * @bufferram: [INTERN] BufferRAM info * @readw: [REPLACEABLE] hardware specific function for read short * @writew: [REPLACEABLE] hardware specific function for write short * @command: [REPLACEABLE] hardware specific function for writing * commands to the chip * @wait: [REPLACEABLE] hardware specific function for wait on ready * @read_bufferram: [REPLACEABLE] hardware specific function for BufferRAM Area * @write_bufferram: [REPLACEABLE] hardware specific function for BufferRAM Area * @read_word: [REPLACEABLE] hardware specific function for read * register of OneNAND * @write_word: [REPLACEABLE] hardware specific function for write * register of OneNAND * @mmcontrol: sync burst read function * @block_markbad: function to mark a block as bad * @scan_bbt: [REPLACEALBE] hardware specific function for scanning * Bad block Table * @chip_lock: [INTERN] spinlock used to protect access to this * structure and the chip * @wq: [INTERN] wait queue to sleep on if a OneNAND * operation is in progress * @state: [INTERN] the current state of the OneNAND device * @page_buf: data buffer * @subpagesize: [INTERN] holds the subpagesize * @ecclayout: [REPLACEABLE] the default ecc placement scheme * @bbm: [REPLACEABLE] pointer to Bad Block Management * @priv: [OPTIONAL] pointer to private chip date */ struct onenand_chip { void __iomem *base; unsigned int chipsize; unsigned int device_id; unsigned int version_id; unsigned int density_mask; unsigned int options; unsigned int erase_shift; unsigned int page_shift; unsigned int ppb_shift; /* Pages per block shift */ unsigned int page_mask; uint (*command)(struct mtd_info *mtd, int cmd, loff_t address); unsigned int (*read)(void __iomem *addr); void (*write)(unsigned int value, void __iomem *addr); int (*scan_bbt)(struct mtd_info *mtd); unsigned char *page_buf; unsigned char *oob_buf; int subpagesize; struct nand_ecclayout *ecclayout; void *bbm; void *priv; }; //---------------------Onenand.c (uboot1.1.16_256m-for36---v1.01\drivers\onenand)-------------------- onenand_info_t onenand_info[CFG_MAX_ONENAND_DEVICE]; static struct onenand_chip onenand_chip[CFG_MAX_ONENAND_DEVICE]; Static ulong base_address[CFG_MAX_ONENAND_DEVICE] = CFG_ONENAND_BASE_LIST void onenand_init(void) { int i; unsigned int size = 0; for (i = 0; i < CFG_MAX_ONENAND_DEVICE; i++) { onenand_init_chip(&onenand_info[i], &onenand_chip[i], base_address[i]); size += onenand_info[i].size; //计算onenand的总大小 if (onenand_curr_device == -1) onenand_curr_device = i; } printf("%lu MB\n", size / (1024 * 1024)); //打印信息 } static void onenand_init_chip(struct mtd_info *mtd, struct onenand_chip *onenand, ulong base_addr) { mtd->priv = onenand; //同样将nand_chip绑定到mtd_info结构中 if (board_onenand_init(onenand)) return; if (onenand_scan(mtd, CFG_MAX_ONENAND_DEVICE) == 0) { if (!mtd->name) mtd->name = (char *)default_onenand_name; } else mtd->name = NULL; } //----------------------Onenand.c (uboot1.1.16_256m-for36---v1.01\cpu\s3c64xx)---------------------- /* * Board-specific NAND initialization. The following members of the * argument are board-specific (per include/linux/mtd/nand.h): * - base : address that OneNAND is located at. * - scan_bbt: board specific bad block scan function. * Members with a "?" were not set in the merged testing-NAND branch, * so they are not set here either. */ int board_onenand_init (struct onenand_chip *onenand)//直接操作底层寄存器初始化onenand { int value; onenand->base = (void __iomem *)CFG_ONENAND_BASE; onenand->options |= (ONENAND_READ_BURST | ONENAND_CHECK_BAD | ONENAND_PIPELINE_AHEAD); onenand->scan_bbt = s3c_scan_bbt; /*** Initialize Controller ***/ /* SYSCON */ value = readl(ELFIN_CLOCK_POWER_BASE + CLK_DIV0_OFFSET); value = (value & ~(3 << 16)) | (1 << 16); writel(value, ELFIN_CLOCK_POWER_BASE + CLK_DIV0_OFFSET); #if defined(CONFIG_S3C6410) || defined(CONFIG_S3C6430) writel(ONENAND_FLASH_AUX_WD_DISABLE, ONENAND_REG_FLASH_AUX_CNTRL); #endif /* Cold Reset */ writel(ONENAND_MEM_RESET_COLD, onenand->base + ONENAND_REG_MEM_RESET); /* Access Clock Register */ writel(ONENAND_ACC_CLOCK_134_67, onenand->base + ONENAND_REG_ACC_CLOCK); /* FBA, FPA, FSA, DBS_DFS Width Register */ set_addr_width_regs(onenand); /* Enable Interrupts */ writel(0x3ff, onenand->base + ONENAND_REG_INT_ERR_MASK); writel(ONENAND_INT_PIN_ENABLE, onenand->base + ONENAND_REG_INT_PIN_ENABLE); writel(readl(onenand->base + ONENAND_REG_INT_ERR_MASK) & ~(ONENAND_INT_ERR_RDY_ACT), onenand->base + ONENAND_REG_INT_ERR_MASK); /* Memory Device Configuration Register */ value = (ONENAND_MEM_CFG_SYNC_READ | ONENAND_MEM_CFG_BRL_4 | \ ONENAND_MEM_CFG_BL_16 | ONENAND_MEM_CFG_IOBE | \ ONENAND_MEM_CFG_INT_HIGH | ONENAND_MEM_CFG_RDY_HIGH); writel(value, onenand->base + ONENAND_REG_MEM_CFG); /* Burst Length Register */ writel(ONENAND_BURST_LEN_16, onenand->base + ONENAND_REG_BURST_LEN); return 0; } #endif /* CONFIG_ONENAND * //--------------------S3c_onenand.c (uboot1.1.16_256m-for36---v1.01\drivers\onenand)--------------- /** * onenand_scan - [OneNAND Interface] Scan for the OneNAND device * @param mtd MTD device structure * @param maxchips Number of chips to scan for * * This fills out all the not initialized function pointers * with the defaults. * The flash ID is read and the mtd/chip structures are * filled with the appropriate values. */ int onenand_scan(struct mtd_info *mtd, int maxchips)//链接底层操作函数,和oob相关属//性及分布的设置、最后进行坏块扫描 { int i; struct onenand_chip *chip = mtd->priv; if (!chip->read) chip->read = onenand_readl; if (!chip->write) chip->write = onenand_writel; if (!chip->command) chip->command = onenand_command; if (onenand_probe(mtd)) //这是探针函数,就是在onenand注册序列中扫描 //当前onenand是否存在 return -ENXIO; /* Allocate buffers, if necessary */ if (!chip->page_buf) { size_t len; len = mtd->writesize + mtd->oobsize; chip->page_buf = kmalloc(len, GFP_KERNEL); if (!chip->page_buf) { printk(KERN_ERR "onenand_scan(): Can't allocate page_buf\n"); return -ENOMEM; } chip->options |= ONENAND_PAGEBUF_ALLOC; } if (!chip->oob_buf) { chip->oob_buf = kmalloc(mtd->oobsize, GFP_KERNEL); if (!chip->oob_buf) { printk(KERN_ERR "onenand_scan(): Can't allocate oob_buf\n"); return -ENOMEM; } chip->options |= ONENAND_OOBBUF_ALLOC; } /* * Allow subpage writes up to oobsize. */ switch (mtd->oobsize) { case 64: chip->ecclayout = &onenand_oob_64; mtd->subpage_sft = 2; break; case 32: chip->ecclayout = &onenand_oob_32; mtd->subpage_sft = 1; break; default: printk(KERN_WARNING "No OOB scheme defined for oobsize %d\n", mtd->oobsize); mtd->subpage_sft = 0; /* To prevent kernel oops */ chip->ecclayout = &onenand_oob_32; break; } chip->subpagesize = mtd->writesize >> mtd->subpage_sft; /* * The number of bytes available for a client to place data into * the out of band area */ chip->ecclayout->oobavail = 0; for (i = 0; chip->ecclayout->oobfree[i].length; i++) chip->ecclayout->oobavail += chip->ecclayout->oobfree[i].length; mtd->oobavail = chip->ecclayout->oobavail; mtd->ecclayout = chip->ecclayout; /* Fill in remaining MTD driver data */ mtd->type = MTD_NANDFLASH; mtd->flags = MTD_CAP_NANDFLASH; mtd->ecctype = MTD_ECC_SW; mtd->erase = onenand_erase; mtd->point = NULL; mtd->unpoint = NULL; mtd->read = onenand_read; mtd->write = onenand_write; mtd->read_oob = onenand_read_oob; mtd->write_oob = onenand_write_oob; mtd->sync = onenand_sync; if (chip->options & ONENAND_CHECK_BAD) mtd->block_isbad = onenand_block_isbad; /* Unlock whole block */ onenand_unlock_all(mtd); return chip->scan_bbt(mtd); } 10.初始化SD卡 //--------------------------Board.c (uboot1.1.16_256m-for36---v1.01\lib_arm)---------------------------- #if defined(CONFIG_BOOT_MOVINAND) uint *magic = (uint *) (PHYS_SDRAM_1); #endif #if defined(CONFIG_BOOT_MOVINAND) puts ("SD/MMC: "); if ((0x24564236 == magic[0]) && (0x20764316 == magic[1])) { printf("Boot up for burning\n"); } else { movi_set_capacity(); movi_set_ofs(MOVI_TOTAL_BLKCNT); movi_init(); } #endif //-----------------------Smdk6410.h (uboot1.1.16_256m-for36---v1.01\include\configs)---------------- #define MEMORY_BASE_ADDRESS 0x50000000 #define PHYS_SDRAM_1 MEMORY_BASE_ADDRESS /* SDRAM Bank #1 */ //-------------------------Cmd_movi.c (uboot1.1.16_256m-for36---v1.01\common)---------------------- struct movi_offset_t ofsinfo; //---------------------------Movi.c (uboot1.1.16_256m-for36---v1.01\cpu\s3c64xx)---------------------- uint movi_hc = 0; void movi_set_capacity(void) { #if defined(CONFIG_S3C6400) if (MOVI_HIGH_CAPACITY == 2) #else if (MOVI_HIGH_CAPACITY & 0x1) #endif movi_hc = 1; } int movi_set_ofs(uint last) { int changed = 0; if (ofsinfo.last != last) { ofsinfo.last = last - (eFUSE_SIZE / MOVI_BLKSIZE); //0x0C004000-0x4-1024/512 ofsinfo.bl1 = ofsinfo.last - MOVI_BL1_BLKCNT; //-8 ofsinfo.env = ofsinfo.bl1 - MOVI_ENV_BLKCNT; ofsinfo.bl2 = ofsinfo.bl1 - (MOVI_BL2_BLKCNT + MOVI_ENV_BLKCNT); //-(512+0x80000/512) ofsinfo.kernel = ofsinfo.bl2 - MOVI_ZIMAGE_BLKCNT; ofsinfo.rootfs = ofsinfo.kernel - MOVI_ROOTFS_BLKCNT; changed = 1; } return changed; } int movi_init(void) { hsmmc_set_gpio(); hsmmc_reset(); if (hsmmc_init()) { printf("\nCard Initialization failed.\n"); return -1; } return 1; } //----------------------------Movi.h (uboot1.1.16_256m-for36---v1.01\include)--------------------------- struct movi_offset_t { uint last; uint bl1; uint env; uint bl2; uint kernel; uint rootfs; }; /* size information */ #if defined(CONFIG_S3C6400) #define SS_SIZE (4 * 1024) #define eFUSE_SIZE (2 * 1024) // 1.5k eFuse, 0.5k reserved #else #define SS_SIZE (8 * 1024) #define eFUSE_SIZE (1 * 1024) // 0.5k eFuse, 0.5k reserved` #endif /* movinand definitions */ #define MOVI_BLKSIZE 512 #define PART_SIZE_BL (256 * 1024) #define PART_SIZE_KERNEL (4 * 1024 * 1024) #define PART_SIZE_ROOTFS (8 * 1024 * 1024) #define MOVI_BL1_BLKCNT (SS_SIZE / MOVI_BLKSIZE) //8 #define MOVI_ENV_BLKCNT (CFG_ENV_SIZE / MOVI_BLKSIZE) //0x80000/512 #define MOVI_BL2_BLKCNT (PART_SIZE_BL / MOVI_BLKSIZE) //512 #define MOVI_ZIMAGE_BLKCNT (PART_SIZE_KERNEL / MOVI_BLKSIZE) //8kb #define MOVI_ROOTFS_BLKCNT (PART_SIZE_ROOTFS / MOVI_BLKSIZE) //16kb #if defined(CONFIG_S3C6400) || defined(CONFIG_S3C6410) || defined(CONFIG_S3C6430) #define TCM_BASE 0x0C004000 #define BL2_BASE 0x57E00000 #elif defined(CONFIG_S3C2450) || defined(CONFIG_S3C2416) #define TCM_BASE 0x40004000 #define BL2_BASE 0x33E00000 #else # error TCM_BASE or BL2_BASE is not defined #endif #ifdef CONFIG_BOOT_MOVINAND #define MOVI_TOTAL_BLKCNT *((volatile unsigned int*)(TCM_BASE - 0x4)) #define MOVI_HIGH_CAPACITY *((volatile unsigned int*)(TCM_BASE - 0x8)) #else #define MOVI_TOTAL_BLKCNT 7864320 // 7864320 // 3995648 // 1003520 /* static movinand total block count: for writing to movinand when nand boot */ #define MOVI_HIGH_CAPACITY 0 #endif //-----------------------Hs_mmc.c (uboot1.1.16_256m-for36---v1.01\cpu\s3c64xx)---------------------- #define s3c_hsmmc_writeb(v,x) writeb((v),(ELFIN_HSMMC_BASE + (HSMMC_CHANNEL * 0x100000)) + (x)) //*((volatile u8 *)(0x7C200000+0*0x100000+0x2f))=0x3 mmc软复位 void hsmmc_set_gpio (void)//设置GPIO口 { u32 reg; #if (HSMMC_CHANNEL == 0) //接在GPG上的MMC0 reg = readl(GPGCON) & 0xf0000000; //初始化IO接口为MMC0,即SD卡 writel(reg | 0x02222222, GPGCON); //GPG0-6 reg = readl(GPGPUD) & 0xfffff000; //GPG0-5的IO口上拉下拉禁止 writel(reg, GPGPUD); #elif (HSMMC_CHANNEL == 1) //接在GPH上的MMC1 writel(0x00222222, GPHCON0); //初始化IO接口为MMC1,即SD卡 writel(0x00000000, GPHCON1); //GPH0-5 reg = readl(GPHPUD) & 0xfffff000; //GPH0-5的IO口上拉下拉禁止 writel(reg, GPHPUD); #else printf("### HS-MMC channel is not defined!\n"); #endif } void hsmmc_reset (void)//mmc软复位 { s3c_hsmmc_writeb(0x3, HM_SWRST); //#define HM_SWRST (0x2f) } int hsmmc_init (void) { u32 reg; uint width; width = 4; HCLK = get_HCLK(); //获取HCLK dbg("HCLK = %08lu\n", HCLK); hsmmc_clock_onoff(0); reg = readl(SCLK_GATE); //读0x7e00f038 writel(reg | (1<<27), SCLK_GATE); //打开MMC0特殊时钟的旁路门控 set_clock(SD_EPLL, 0x80); //SD_EPLL值为2 s3c_hsmmc_writeb(0xe, HM_TIMEOUTCON); set_hostctl_speed(NORMAL); InterruptEnable(0xff, 0xff); dbg("HM_NORINTSTS = %x\n", s3c_hsmmc_readw(HM_NORINTSTS)); /* MMC_GO_IDLE_STATE */ issue_command(MMC_GO_IDLE_STATE, 0x00, 0, 0); ocr_check = 1; if (set_mmc_ocr()) { mmc_card = 1; dbg("MMC card is detected\n"); } else if (set_sd_ocr()) { mmc_card = 0; dbg("SD card is detected\n"); } else { printf("0 MB\n"); return 0; } ocr_check = 0; /* Check the attached card and place the card * in the IDENT state rHM_RSPREG0 */ issue_command(MMC_ALL_SEND_CID, 0, 0, MMC_RSP_R2); /* Manufacturer ID */ card_mid = (s3c_hsmmc_readl(HM_RSPREG3) >> 16) & 0xFF; dbg("Product Name : %c%c%c%c%c%c\n", ((s3c_hsmmc_readl(HM_RSPREG2) >> 24) & 0xFF), ((s3c_hsmmc_readl(HM_RSPREG2) >> 16) & 0xFF), ((s3c_hsmmc_readl(HM_RSPREG2) >> 8) & 0xFF), (s3c_hsmmc_readl(HM_RSPREG2) & 0xFF), ((s3c_hsmmc_readl(HM_RSPREG1) >> 24) & 0xFF), ((s3c_hsmmc_readl(HM_RSPREG1) >> 16) & 0xFF)); // Send RCA(Relative Card Address). It places the card in the STBY state rca = (mmc_card) ? 0x0001 : 0x0000; issue_command(MMC_SET_RELATIVE_ADDR, rca<<16, 0, MMC_RSP_R1); if (!mmc_card) rca = (s3c_hsmmc_readl(HM_RSPREG0) >> 16) & 0xFFFF; dbg("Enter to the Stand-by State\n"); issue_command(MMC_SEND_CSD, rca<<16, 0, MMC_RSP_R2); if (mmc_card) { mmc_spec = (s3c_hsmmc_readl(HM_RSPREG3) >> 18) & 0xF; dbg("mmc_spec=%d\n", mmc_spec); } issue_command(MMC_SELECT_CARD, rca<<16, 0, MMC_RSP_R1); dbg("Enter to the Transfer State\n"); display_card_info(); /* Operating Clock setting */ clock_config(SD_EPLL, 2); // Divisor 1 = Base clk /2 ,Divisor 2 = Base clk /4, Divisor 4 = Base clk /8 ... while (set_bus_width(width)); while (!check_card_status()); /* MMC_SET_BLOCKLEN */ while (!issue_command(MMC_SET_BLOCKLEN, 512, 0, MMC_RSP_R1)); s3c_hsmmc_writew(0xffff, HM_NORINTSTS); return 0; } static void hsmmc_clock_onoff (int on) { u16 reg16; if (on == 0) { //关闭SD卡时钟 //#define s3c_hsmmc_readw(x) readw((ELFIN_HSMMC_BASE + (HSMMC_CHANNEL * //0x100000)) + (x)) 0x7c200000+0*0x100000 reg16 = s3c_hsmmc_readw(HM_CLKCON) & ~(0x1<<2); //HM_CLKCON值为0x2c s3c_hsmmc_writew(reg16, HM_CLKCON); //禁止SD Clock } else { //打开SD卡时钟 reg16 = s3c_hsmmc_readw(HM_CLKCON); s3c_hsmmc_writew(reg16 | (0x1<<2), HM_CLKCON); //使能SD Clock while (1) { reg16 = s3c_hsmmc_readw(HM_CLKCON); //等待时钟稳定 if (reg16 & (0x1<<3)) /* SD_CLKSRC is Stable */ break; } } } static void set_clock (uint clksrc, uint div) { u16 reg16; uint i; #if defined(CONFIG_S3C6400) s3c_hsmmc_writel(0xC0000100 | (clksrc << 4), HM_CONTROL2); // feedback control off s3c_hsmmc_writel(0x00000000, HM_CONTROL3); #else s3c_hsmmc_writel(0xC0004100 | (clksrc << 4), HM_CONTROL2); // rx feedback control s3c_hsmmc_writel(0x00008080, HM_CONTROL3); // Low clock: 00008080 s3c_hsmmc_writel(0x3 << 16, HM_CONTROL4); #endif s3c_hsmmc_writew(s3c_hsmmc_readw(HM_CLKCON) & ~(0xff << 8), HM_CLKCON); /* SDCLK Value Setting + Internal Clock Enable */ s3c_hsmmc_writew(((div<<8) | 0x1), HM_CLKCON); /* CheckInternalClockStable */ for (i=0; i<0x10000; i++) { reg16 = s3c_hsmmc_readw(HM_CLKCON); if (reg16 & 0x2) break; } if (i == 0x10000) printf("internal clock stabilization failed\n"); dbg("HM_CONTROL2(0x80) = 0x%08x\n", s3c_hsmmc_readl(HM_CONTROL2)); dbg("HM_CONTROL3(0x84) = 0x%08x\n", s3c_hsmmc_readl(HM_CONTROL3)); dbg("HM_CLKCON (0x2c) = 0x%04x\n", s3c_hsmmc_readw(HM_CLKCON)); hsmmc_clock_onoff(1); } 以上关于SD卡的底层操作,在这里也不展开来讲了,到驱动的章节中再详细讲解关于SD卡的底层操作。 11.初始化SPI接口的flash芯片 //---------------------------Board.c (uboot1.1.16_256m-for36---v1.01\lib_arm)--------------------------- #ifdef CONFIG_HAS_DATAFLASH AT91F_DataflashInit(); dataflash_print_info(); //打印属性信息 #endif //----------------------------Dataflash.h (uboot1.1.16_256m-for36---v1.01\include)---------------------- typedef struct _AT91S_DATAFLASH_INFO { AT91S_DataflashDesc Desc; AT91S_DataflashFeatures Device; /* Pointer on a dataflash features array */ unsigned long logical_address; unsigned int id; /* device id */ } AT91S_DATAFLASH_INFO, *AT91PS_DATAFLASH_INFO; //--------------------------Dataflash.c (uboot1.1.16_256m-for36---v1.01\drivers)------------------------- AT91S_DATAFLASH_INFO dataflash_info[CFG_MAX_DATAFLASH_BANKS]; int AT91F_DataflashInit (void) { int i, j; int dfcode; AT91F_SpiInit (); for (i = 0; i < CFG_MAX_DATAFLASH_BANKS; i++) { dataflash_info[i].Desc.state = IDLE; dataflash_info[i].id = 0; dataflash_info[i].Device.pages_number = 0; dfcode = AT91F_DataflashProbe (cs[i][1], &dataflash_info[i].Desc); //侦测型号 switch (dfcode) { //根据不同型号,赋值相关属性 case AT45DB161: dataflash_info[i].Device.pages_number = 4096; dataflash_info[i].Device.pages_size = 528; dataflash_info[i].Device.page_offset = 10; dataflash_info[i].Device.byte_mask = 0x300; dataflash_info[i].Device.cs = cs[i][1]; dataflash_info[i].Desc.DataFlash_state = IDLE; dataflash_info[i].logical_address = cs[i][0]; dataflash_info[i].id = dfcode; break; case AT45DB321: dataflash_info[i].Device.pages_number = 8192; dataflash_info[i].Device.pages_size = 528; dataflash_info[i].Device.page_offset = 10; dataflash_info[i].Device.byte_mask = 0x300; dataflash_info[i].Device.cs = cs[i][1]; dataflash_info[i].Desc.DataFlash_state = IDLE; dataflash_info[i].logical_address = cs[i][0]; dataflash_info[i].id = dfcode; break; case AT45DB642: dataflash_info[i].Device.pages_number = 8192; dataflash_info[i].Device.pages_size = 1056; dataflash_info[i].Device.page_offset = 11; dataflash_info[i].Device.byte_mask = 0x700; dataflash_info[i].Device.cs = cs[i][1]; dataflash_info[i].Desc.DataFlash_state = IDLE; dataflash_info[i].logical_address = cs[i][0]; dataflash_info[i].id = dfcode; break; case AT45DB128: dataflash_info[i].Device.pages_number = 16384; dataflash_info[i].Device.pages_size = 1056; dataflash_info[i].Device.page_offset = 11; dataflash_info[i].Device.byte_mask = 0x700; dataflash_info[i].Device.cs = cs[i][1]; dataflash_info[i].Desc.DataFlash_state = IDLE; dataflash_info[i].logical_address = cs[i][0]; dataflash_info[i].id = dfcode; break; default: break; } /* set the last area end to the dataflash size*/ area_list[NB_DATAFLASH_AREA -1].end = (dataflash_info[i].Device.pages_number * dataflash_info[i].Device.pages_size)-1; /* set the area addresses */ for(j = 0; j dataflash_info[i].Device.area_list[j].start = area_list[j].start + dataflash_info[i].logical_address; dataflash_info[i].Device.area_list[j].end = area_list[j].end + dataflash_info[i].logical_address; dataflash_info[i].Device.area_list[j].protected = area_list[j].protected; } } return (1); } 12.重定位环境变量 //-------------------------Board.c (uboot1.1.16_256m-for36---v1.01\lib_arm)----------------------------- /* initialize environment */ env_relocate (); //-----------------------Environment.h (uboot1.1.16_256m-for36---v1.01\include)----------------------- #define ENV_SIZE (CFG_ENV_SIZE - ENV_HEADER_SIZE) //0x80000-0x4 typedef struct environment_s { unsigned long crc; /* CRC32 over data bytes */ #ifdef CFG_REDUNDAND_ENVIRONMENT unsigned char flags; /* active/obsolete flags */ #endif unsigned char data[ENV_SIZE]; /* Environment data */ } env_t; //------------------------Env_common.c (uboot1.1.16_256m-for36---v1.01\common)-------------------- void env_relocate (void) { DEBUGF ("%s[%d] offset = 0x%lx\n", __FUNCTION__,__LINE__, gd->reloc_off); //打印重定位地址 #ifdef CONFIG_AMIGAONEG3SE enable_nvram(); #endif #ifdef ENV_IS_EMBEDDED /* * The environment buffer is embedded with the text segment, * just relocate the environment pointer */ env_ptr = (env_t *)((ulong)env_ptr + gd->reloc_off); //内嵌的环境变量只做静态偏移 DEBUGF ("%s[%d] embedded ENV at %p\n", __FUNCTION__,__LINE__,env_ptr); #else /* * We must allocate a buffer for the environment */ //#define CFG_ENV_SIZE 0x80000 env_ptr = (env_t *)malloc (CFG_ENV_SIZE); //非内嵌的环境变量,要动态申请空间 DEBUGF ("%s[%d] malloced ENV at %p\n", __FUNCTION__,__LINE__,env_ptr); #endif /* * After relocation to RAM, we can always use the "memory" functions */ env_get_char = env_get_char_memory; //环境变量内存值获取函数 if (gd->env_valid == 0) { //打印信息 #if defined(CONFIG_GTH) || defined(CFG_ENV_IS_NOWHERE) /* Environment not changable */ puts ("Using default environment\n\n"); #else puts ("*** Warning - bad CRC, using default environment\n\n"); SHOW_BOOT_PROGRESS (-1); #endif if (sizeof(default_environment) > ENV_SIZE) { puts ("*** Error - default environment is too large\n\n"); return; } memset (env_ptr, 0, sizeof(env_t)); //清除环境变量结构体 memcpy (env_ptr->data, //将默认环境变量参数拷贝到结构体中 default_environment, sizeof(default_environment)); #ifdef CFG_REDUNDAND_ENVIRONMENT env_ptr->flags = 0xFF; #endif env_crc_update (); gd->env_valid = 1; } else { env_relocate_spec (); } gd->env_addr = (ulong)&(env_ptr->data); //环境变量数组地址绑定到gd_t结构中 #ifdef CONFIG_AMIGAONEG3SE disable_nvram(); #endif } //---------------------Env_common.c (uboot1.1.16_256m-for36---v1.01\common)----------------------- uchar default_environment[] = { #ifdef CONFIG_BOOTARGS "bootargs=" CONFIG_BOOTARGS "\0" #endif #ifdef CONFIG_BOOTCOMMAND "bootcmd=" CONFIG_BOOTCOMMAND "\0" #endif #ifdef CONFIG_RAMBOOTCOMMAND "ramboot=" CONFIG_RAMBOOTCOMMAND "\0" #endif #ifdef CONFIG_NFSBOOTCOMMAND "nfsboot=" CONFIG_NFSBOOTCOMMAND "\0" #endif #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0) "bootdelay=" MK_STR(CONFIG_BOOTDELAY) "\0" #endif #if defined(CONFIG_BAUDRATE) && (CONFIG_BAUDRATE >= 0) "baudrate=" MK_STR(CONFIG_BAUDRATE) "\0" #endif #ifdef CONFIG_LOADS_ECHO "loads_echo=" MK_STR(CONFIG_LOADS_ECHO) "\0" #endif #ifdef CONFIG_ETHADDR "ethaddr=" MK_STR(CONFIG_ETHADDR) "\0" #endif #ifdef CONFIG_ETH1ADDR "eth1addr=" MK_STR(CONFIG_ETH1ADDR) "\0" #endif #ifdef CONFIG_ETH2ADDR "eth2addr=" MK_STR(CONFIG_ETH2ADDR) "\0" #endif #ifdef CONFIG_ETH3ADDR "eth3addr=" MK_STR(CONFIG_ETH3ADDR) "\0" #endif #ifdef CONFIG_IPADDR "ipaddr=" MK_STR(CONFIG_IPADDR) "\0" #endif #ifdef CONFIG_SERVERIP "serverip=" MK_STR(CONFIG_SERVERIP) "\0" #endif #ifdef CFG_AUTOLOAD "autoload=" CFG_AUTOLOAD "\0" #endif #ifdef CONFIG_PREBOOT "preboot=" CONFIG_PREBOOT "\0" #endif #ifdef CONFIG_ROOTPATH "rootpath=" MK_STR(CONFIG_ROOTPATH) "\0" #endif #ifdef CONFIG_GATEWAYIP "gatewayip=" MK_STR(CONFIG_GATEWAYIP) "\0" #endif #ifdef CONFIG_NETMASK "netmask=" MK_STR(CONFIG_NETMASK) "\0" #endif #ifdef CONFIG_HOSTNAME "hostname=" MK_STR(CONFIG_HOSTNAME) "\0" #endif #ifdef CONFIG_BOOTFILE "bootfile=" MK_STR(CONFIG_BOOTFILE) "\0" #endif #ifdef CONFIG_LOADADDR "loadaddr=" MK_STR(CONFIG_LOADADDR) "\0" #endif #ifdef CONFIG_CLOCKS_IN_MHZ "clocks_in_mhz=1\0" #endif #if defined(CONFIG_PCI_BOOTDELAY) && (CONFIG_PCI_BOOTDELAY > 0) "pcidelay=" MK_STR(CONFIG_PCI_BOOTDELAY) "\0" #endif #ifdef CONFIG_EXTRA_ENV_SETTINGS CONFIG_EXTRA_ENV_SETTINGS #endif "\0" }; #ifdef CONFIG_AMIGAONEG3SE uchar env_get_char_memory (int index) { uchar retval; enable_nvram(); if (gd->env_valid) { retval = ( *((uchar *)(gd->env_addr + index)) ); } else { retval = ( default_environment[index] ); } disable_nvram(); return retval; } #else uchar env_get_char_memory (int index) { if (gd->env_valid) { return ( *((uchar *)(gd->env_addr + index)) ); } else { return ( default_environment[index] ); } } #endif void env_crc_update (void) { env_ptr->crc = crc32(0, env_ptr->data, ENV_SIZE); //更新环境变量结构体CRC校验值 } 13.前面初始化VFD的帧缓冲以后,这里初始化VFD显示屏,由于很少使用这种屏了,所以这里我们没有实现这个函数 //---------------------------Board.c (uboot1.1.16_256m-for36---v1.01\lib_arm)--------------------------- #ifdef CONFIG_VFD /* must do this after the framebuffer is allocated */ drv_vfd_init(); #endif /* CONFIG_VFD */ 14.设置IP地址和MAC地址 //---------------------------Board.c (uboot1.1.16_256m-for36---v1.01\lib_arm)--------------------------- /* IP Address */ gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr"); //bd_t结构体绑定环境变量中的IP地址 /* MAC Address */ { int i; ulong reg; char *s, *e; char tmp[64]; i = getenv_r ("ethaddr", tmp, sizeof (tmp));//bd_t结构体绑定环境变量中的物理地址0 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 //bd_t结构体绑定环境变量中的物理地址1 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 } //------------------------------Net.h (uboot1.1.16_256m-for36---v1.01\include)---------------------------- typedef ulong IPaddr_t; //--------------------------------Net.c (uboot1.1.16_256m-for36---v1.01\net)------------------------------- IPaddr_t string_to_ip(char *s)//字符转换IP地址 { IPaddr_t addr; char *e; int i; if (s == NULL) return(0); for (addr=0, i=0; i<4; ++i) { ulong val = s ? simple_strtoul(s, &e, 10) : 0; addr <<= 8; addr |= (val & 0xFF); if (s) { s = (*e) ? e+1 : e; } } return (htonl(addr)); } IPaddr_t getenv_IPaddr (char *var) { return (string_to_ip(getenv(var))); //返回IP地址的环境变量 } //--------------------------Cmd_nvedit.c (uboot1.1.16_256m-for36---v1.01\common)-------------------- /************************************************************************ * Look up variable from environment, * return address of storage for that variable, * or NULL if not found */ char *getenv (char *name) { int i, nxt; WATCHDOG_RESET(); for (i=0; env_get_char(i) != '\0'; i=nxt+1) { int val; for (nxt=i; env_get_char(nxt) != '\0'; ++nxt) { //搜索单条环境变量 if (nxt >= CFG_ENV_SIZE) { return (NULL); } } if ((val=envmatch((uchar *)name, i)) < 0) //匹配是否所需的环境变量,并返回其值 continue; return ((char *)env_get_addr(val)); } return (NULL); 15.设备初始化 //---------------------------Board.c (uboot1.1.16_256m-for36---v1.01\lib_arm)--------------------------- devices_init (); /* get the devices list going. */ //---------------------------Devices.c (uboot1.1.16_256m-for36---v1.01\common)------------------------ int devices_init (void)//主要是用来初始化并注册设备,这里我们只展开drv_lcd_init分析 { #ifndef CONFIG_ARM /* already relocated for current ARM implementation */ ulong relocation_offset = gd->reloc_off; //读取环境变量偏移地址 int i; /* relocate device name pointers */ for (i = 0; i < (sizeof (stdio_names) / sizeof (char *)); ++i) { //重定位标准输入输出名的内容 stdio_names[i] = (char *) (((ulong) stdio_names[i]) + relocation_offset); } #endif /* Initialize the list */ devlist = ListCreate (sizeof (device_t)); //创建一个新的设备list if (devlist == NULL) { eputs ("Cannot initialize the list of devices!\n"); return -1; } //包含I2C,VGA,键盘,串口,usb接口设备初始化等等。 #if defined(CONFIG_HARD_I2C) || defined(CONFIG_SOFT_I2C) i2c_init (CFG_I2C_SPEED, CFG_I2C_SLAVE); vga_init(); #endif #ifdef CONFIG_LCD drv_lcd_init (); //初始化LCD设备,并注册,其他基本也是同一个套路 #endif #if defined(CONFIG_VIDEO) || defined(CONFIG_CFB_CONSOLE) drv_video_init (); #endif #ifdef CONFIG_KEYBOARD drv_keyboard_init (); #endif #ifdef CONFIG_LOGBUFFER drv_logbuff_init (); #endif drv_system_init (); #ifdef CONFIG_SERIAL_MULTI serial_devices_init (); #endif #ifdef CONFIG_USB_TTY drv_usbtty_init (); #endif #ifdef CONFIG_NETCONSOLE drv_nc_init (); #endif if(0) { uchar chip=0xEC; uint addr=0x04; int alen=1; uchar buffer[1]={0x55}; int len=1; //i2c_init (CFG_I2C_SPEED, CFG_I2C_SLAVE); i2c_write (chip, addr, alen, buffer, len); //printf("iic_write %d \n",f); } return (0); } list_t ListCreate (int elementSize)//创建一个设备列表 { list_t list; list = (list_t) (NewHandle (sizeof (ListStruct))); /* create empty list */ if (list) { (*list)->signature = LIST_SIGNATURE; (*list)->numItems = 0; (*list)->listSize = 0; (*list)->itemSize = elementSize; (*list)->percentIncrease = kDefaultAllocationPercentIncrease; (*list)->minNumItemsIncrease = kDefaultAllocationminNumItemsIncrease; } return list; } int device_register (device_t * dev)//注册设备 { ListInsertItem (devlist, dev, LIST_END); //把这个设备插入到设备列表的尾部 return 0; } //-----------------------Lists.h (uboot1.1.16_256m-for36---v1.01\include)--------------------------------- typedef struct ListStructTag { int signature; /* debugging aid */ int percentIncrease; /* %of current size to increase by when list is out of space */ int minNumItemsIncrease; /* fixed number of items to increase by when list is out of space */ int listSize; /* number of items than can fit in the currently allocated memory */ int itemSize; /* the size of each item in the list (same for every item) */ int numItems; /* number of items currently in the list */ unsigned char itemList[1]; /* resizable array of list elements */ } ListStruct; typedef struct ListStructTag **list_t; /* The list abstract data type */ //--------------------Devices.h (uboot1.1.16_256m-for36---v1.01\include)-------------------------------- /* Device information */ typedef struct { int flags; /* Device flags: input/output/system */ int ext; /* Supported extensions */ char name[16]; /* Device name */ /* GENERAL functions */ int (*start) (void); /* To start the device */ int (*stop) (void); /* To stop the device */ /* OUTPUT functions */ void (*putc) (const char c); /* To put a char */ void (*puts) (const char *s); /* To put a string (accelerator) */ /* INPUT functions */ int (*tstc) (void); /* To test if a char is ready... */ int (*getc) (void); /* To get that char */ /* Other functions */ void *priv; /* Private extensions */ } device_t; //-------------------------Lcd.c (uboot1.1.16_256m-for36---v1.01\common)------------------------------- /************************************************************************/ /* ** GENERIC Initialization Routines */ /************************************************************************/ int drv_lcd_init (void) { device_t lcddev; int rc; lcd_base = (void *)(gd->fb_base); //从gd_t结构体获取base address of frame buffer lcd_line_length = (panel_info.vl_col * NBITS (panel_info.vl_bpix)) / 8; lcd_init (lcd_base); /* LCD initialization */ //LCD硬件初始化 /* Device initialization */ memset (&lcddev, 0, sizeof (lcddev)); //清除LCD设备结构体 strcpy (lcddev.name, "lcd"); //设置设备名 lcddev.ext = 0; /* No extensions */ lcddev.flags = DEV_FLAGS_OUTPUT; /* Output only */ lcddev.putc = lcd_putc; /* 'putc' function */ lcddev.puts = lcd_puts; /* 'puts' function */ rc = device_register (&lcddev); //注册LCD设备到ListCreate创建的设备列表中 return (rc == 0) ? 1 : rc; } //--------------------------Lcd.h (uboot1.1.16_256m-for36---v1.01\include)------------------------------- #define NBITS(bit_code) (1 << (bit_code)) /* * LCD controller stucture for MPC823 CPU */ typedef struct vidinfo { ushort vl_col; /* Number of columns (i.e. 640) */ ushort vl_row; /* Number of rows (i.e. 480) */ ushort vl_width; /* Width of display area in millimeters */ ushort vl_height; /* Height of display area in millimeters */ /* LCD configuration register */ u_char vl_clkp; /* Clock polarity */ u_char vl_oep; /* Output Enable polarity */ u_char vl_hsp; /* Horizontal Sync polarity */ u_char vl_vsp; /* Vertical Sync polarity */ u_char vl_dp; /* Data polarity */ u_char vl_bpix; /* Bits per pixel, 0 = 1, 1 = 2, 2 = 4, 3 = 8 */ u_char vl_lbw; /* LCD Bus width, 0 = 4, 1 = 8 */ u_char vl_splt; /* Split display, 0 = single-scan, 1 = dual-scan */ u_char vl_clor; /* Color, 0 = mono, 1 = color */ u_char vl_tft; /* 0 = passive, 1 = TFT */ /* Horizontal control register. Timing from data sheet */ ushort vl_wbl; /* Wait between lines */ /* Vertical control register */ u_char vl_vpw; /* Vertical sync pulse width */ u_char vl_lcdac; /* LCD AC timing */ u_char vl_wbf; /* Wait between frames */ } vidinfo_t; extern vidinfo_t panel_info; //------------------------Lists.c (uboot1.1.16_256m-for36---v1.01\common)------------------------------- /*******************************/ /* * returns 1 if the item is inserted, returns 0 if out of memory or * bad arguments were passed. */ int ListInsertItem (list_t list, void *ptrToItem, int itemPosition)//向设备列表插入一个设备 { return ListInsertItems (list, ptrToItem, itemPosition, 1);//在list中的itemPosition处插入 //ptrToItem所指的1个设备 } int ListInsertItems (list_t list, void *ptrToItems, int firstItemPosition,//插入设备列表 int numItemsToInsert) { int numItems = (*list)->numItems; //获取list中的当前item个数 if (firstItemPosition == numItems + 1) firstItemPosition = LIST_END; else if (firstItemPosition > numItems) return 0; if ((*list)->numItems >= (*list)->listSize) { if (!ExpandListSpace (list, -numItemsToInsert)) //扩展list大小 return 0; } if (firstItemPosition == LIST_START) { if (numItems == 0) { /* special case for empty list */ firstItemPosition = LIST_END; } else { firstItemPosition = 1; } } if (firstItemPosition == LIST_END) { /* add at the end of the list */ if (ptrToItems) memcpy (ITEMPTR (list, numItems), ptrToItems, (*list)->itemSize * numItemsToInsert); else memset (ITEMPTR (list, numItems), 0, (*list)->itemSize * numItemsToInsert); (*list)->numItems += numItemsToInsert; } else { /* move part of list up to make room for new item */ memmove (ITEMPTR (list, firstItemPosition - 1 + numItemsToInsert), ITEMPTR (list, firstItemPosition - 1), (numItems + 1 - firstItemPosition) * (*list)->itemSize); if (ptrToItems) memmove (ITEMPTR (list, firstItemPosition - 1), ptrToItems, (*list)->itemSize * numItemsToInsert); else memset (ITEMPTR (list, firstItemPosition - 1), 0, (*list)->itemSize * numItemsToInsert); (*list)->numItems += numItemsToInsert; } return 1; } 16.初始化跳转表 //--------------------------Board.c (uboot1.1.16_256m-for36---v1.01\lib_arm)---------------------------- #ifdef CONFIG_CMC_PU2 load_sernum_ethaddr (); #endif /* CONFIG_CMC_PU2 */ jumptable_init (); //--------------------------Exports.c (uboot1.1.16_256m-for36---v1.01\common)------------------------- void jumptable_init (void)//初始化跳转表,将一些重要的函数和结构绑定到跳转表中 { int i; gd->jt = (void **) malloc (XF_MAX * sizeof (void *)); for (i = 0; i < XF_MAX; i++) gd->jt[i] = (void *) dummy; gd->jt[XF_get_version] = (void *) get_version; gd->jt[XF_malloc] = (void *) malloc; gd->jt[XF_free] = (void *) free; gd->jt[XF_getenv] = (void *) getenv; gd->jt[XF_setenv] = (void *) setenv; gd->jt[XF_get_timer] = (void *) get_timer; gd->jt[XF_simple_strtoul] = (void *) simple_strtoul; gd->jt[XF_udelay] = (void *) udelay; #if defined(CONFIG_I386) || defined(CONFIG_PPC) gd->jt[XF_install_hdlr] = (void *) irq_install_handler; gd->jt[XF_free_hdlr] = (void *) irq_free_handler; #endif /* I386 || PPC */ #if (CONFIG_COMMANDS & CFG_CMD_I2C) gd->jt[XF_i2c_write] = (void *) i2c_write; gd->jt[XF_i2c_read] = (void *) i2c_read; #endif /* CFG_CMD_I2C */ } 17.初始化控制台 //---------------------------Board.c (uboot1.1.16_256m-for36---v1.01\lib_arm)--------------------------- console_init_r (); /* fully init console as a device */ //-------------------------Console.c (uboot1.1.16_256m-for36---v1.01\common)-------------------------- /* Called after the relocation - use desired console functions */ int console_init_r (void) { device_t *inputdev = NULL, *outputdev = NULL; int i, items = ListNumItems (devlist); //取出当前已注册的总设备数 #ifdef CONFIG_SPLASH_SCREEN /* suppress all output if splash screen is enabled and we have a bmp to display */ if (getenv("splashimage") != NULL) //在设备列表中搜索指定的设备作为输出设备 outputdev = search_device (DEV_FLAGS_OUTPUT, "nulldev"); #endif #ifdef CONFIG_SILENT_CONSOLE /* Suppress all output if "silent" mode requested*/ if (gd->flags & GD_FLG_SILENT) outputdev = search_device (DEV_FLAGS_OUTPUT, "nulldev"); #endif /* Scan devices looking for input and output devices */ //在设备列表中通过标识搜索输入输出设备 for (i = 1; (i <= items) && ((inputdev == NULL) || (outputdev == NULL)); i++ ) { device_t *dev = ListGetPtrToItem (devlist, i); if ((dev->flags & DEV_FLAGS_INPUT) && (inputdev == NULL)) { inputdev = dev; } if ((dev->flags & DEV_FLAGS_OUTPUT) && (outputdev == NULL)) { outputdev = dev; } } //将设备输入输出绑定到gd_t结构体的跳转表中 /* Initializes output console first */ if (outputdev != NULL) { console_setfile (stdout, outputdev); console_setfile (stderr, outputdev); } /* Initializes input console */ if (inputdev != NULL) { console_setfile (stdin, inputdev); } gd->flags |= GD_FLG_DEVINIT; /* device initialization completed *///设备初始化完成标识 #ifndef CFG_CONSOLE_INFO_QUIET /* Print information */ //打印输入输出设备信息,stdio_devices在前面console_setfile中 //绑定到设备 puts ("In: "); if (stdio_devices[stdin] == NULL) { puts ("No input devices available!\n"); } else { printf ("%s\n", stdio_devices[stdin]->name); } puts ("Out: "); if (stdio_devices[stdout] == NULL) { puts ("No output devices available!\n"); } else { printf ("%s\n", stdio_devices[stdout]->name); } puts ("Err: "); if (stdio_devices[stderr] == NULL) { puts ("No error devices available!\n"); } else { printf ("%s\n", stdio_devices[stderr]->name); } #endif /* CFG_CONSOLE_INFO_QUIET */ /* Setting environment variables */ for (i = 0; i < 3; i++) { //将输入输出设备添加到环境变量中 setenv (stdio_names[i], stdio_devices[i]->name); } return (0); } 18.混杂平台独立初始化,这里不是混杂平台。 //---------------------------Board.c (uboot1.1.16_256m-for36---v1.01\lib_arm)--------------------------- #if defined(CONFIG_MISC_INIT_R) /* miscellaneous platform dependent initialisations */ misc_init_r (); #endif 19.使能中断 //---------------------------Board.c (uboot1.1.16_256m-for36---v1.01\lib_arm)--------------------------- /* enable exceptions */ enable_interrupts (); //---------------------------Interrupts.c (uboot1.1.16_256m-for36---v1.01\cpu\s3c64xx)---------------- #ifdef CONFIG_USE_IRQ /* enable IRQ interrupts */ void enable_interrupts(void) { unsigned long temp; __asm__ __volatile__("mrs %0, cpsr\n" "bic %0, %0, #0x80\n" "msr cpsr_c, %0":"=r"(temp) ::"memory"); //开IRQ总中断 } /* * disable IRQ/FIQ interrupts * returns true if interrupts had been enabled before we disabled them */ int disable_interrupts(void) { unsigned long old, temp; __asm__ __volatile__("mrs %0, cpsr\n" "orr %1, %0, #0xc0\n" "msr cpsr_c, %1":"=r"(old), "=r"(temp) ::"memory"); return (old & 0x80) == 0; } #else void enable_interrupts(void) { return; } int disable_interrupts(void) { return 0; } #endif 20.获取几个重要的环境变量,如物理地址,内核加载地址和内核文件名等 //-------------------------Board.c (uboot1.1.16_256m-for36---v1.01\lib_arm)----------------------------- /* Perform network card initialisation if necessary */ #ifdef CONFIG_DRIVER_CS8900 //cs8900_get_enetaddr (gd->bd->bi_enetaddr);//初始化CS8900,绑定MAC地址到bi_enetaddr #endif #if defined(CONFIG_DRIVER_SMC91111) || defined (CONFIG_DRIVER_LAN91C96) if (getenv ("ethaddr")) { smc_set_mac_addr(gd->bd->bi_enetaddr); //获取MAC地址 } #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) { //获取kernel下载文件名称 copy_filename (BootFile, s, sizeof (BootFile)); } #endif /* CFG_CMD_NET */ U-Boot环境变量的解释说明 环 境 变 量 解 释 说 明 bootdelay 定义执行自动启动的等候秒数 baudrate 定义串口控制台的波特率 netmask 定义以太网接口的掩码 ethaddr 定义以太网接口的MAC地址 bootfile 定义UBoot启动缺省的kernel下载文件名称 bootargs 定义传递给Linux内核的命令行参数 bootcmd 定义自动启动时执行的几条命令 serverip 定义tftp服务器端的IP地址 ipaddr 定义本地的IP地址 stdin 定义标准输入设备,一般是串口 stdout 定义标准输出设备,一般是串口 stderr 定义标准出错信息输出设备,一般是串口 laodaddr 设置kernel在内存中的加载地址 rd_loadaddr 设置ramdisk在内存中的加载地址 21.板级后期初始化,主要是设置引导延时和引导命令行相关的环境变量 //-------------------------Board.c (uboot1.1.16_256m-for36---v1.01\lib_arm)----------------------------- #ifdef BOARD_LATE_INIT board_late_init (); #endif //-----------Smdk6410.c (uboot1.1.16_256m-for36---v1.01\board\samsung\smdk6410)-------------- #ifdef BOARD_LATE_INIT #if defined(CONFIG_BOOT_NAND) int board_late_init (void) { uint *magic = (uint*)(PHYS_SDRAM_1); //0x50000000 char boot_cmd[100]; if ((0x24564236 == magic[0]) && (0x20764316 == magic[1])) { //判断幻数 sprintf(boot_cmd, "nand erase 0 40000;nand write %08x 0 40000", PHYS_SDRAM_1 + 0x8000); //向引导命令行boot_cmd中加入nand擦除命令 magic[0] = 0; magic[1] = 0; //清除幻数 printf("\nready for self-burning U-Boot image\n\n"); //打印信息 setenv("bootdelay", "0"); //设置启动延时和启动命令行环境变量 setenv("bootcmd", boot_cmd); } return 0; } #elif defined(CONFIG_BOOT_MOVINAND) int board_late_init (void) { uint *magic = (uint*)(PHYS_SDRAM_1); char boot_cmd[100]; int hc; hc = (magic[2] & 0x1) ? 1 : 0; if ((0x24564236 == magic[0]) && (0x20764316 == magic[1])) { sprintf(boot_cmd, "movi init %d %d;movi write u-boot %08x", magic[3], hc, PHYS_SDRAM_1 + 0x8000);//向引导命令行boot_cmd中加入movinand相关的初始化命令 magic[0] = 0; magic[1] = 0; printf("\nready for self-burning U-Boot image\n\n"); setenv("bootdelay", "0"); setenv("bootcmd", boot_cmd); } return 0; } #else int board_late_init (void) { return 0; } #endif #endif 其实上面的magic就是bootparam的头部标识符(也叫幻数) //---------------------------Ft_build.h (uboot1.1.16_256m-for36---v1.01\include)------------------------- struct boot_param_header { u32 magic; /* magic word OF_DT_HEADER */ u32 totalsize; /* total size of DT block */ u32 off_dt_struct; /* offset to structure */ u32 off_dt_strings; /* offset to strings */ u32 off_mem_rsvmap; /* offset to memory reserve map */ u32 version; /* format version */ u32 last_comp_version; /* last compatible version */ /* version 2 fields below */ u32 boot_cpuid_phys; /* Physical CPU id we're booting on */ /* version 3 fields below */ u32 dt_strings_size; /* size of the DT strings block */ }; 在Smdk6410.c (uboot1.1.16_256m-for36---v1.01\board\samsung\smdk6410)的board_init函数中,我们可以看到gd->bd->bi_boot_params = (PHYS_SDRAM_1+0x100);也就是说PHYS_SDRAM_1处存放了boot_params,而它的头部就是boot_param_header,并且在这个头部的第一个成员存放的就是我们的bootparams的标识符,幻数magic。 22.网络初始化:各种不同的网络设备的初始化,并注册网络设备,这里我们只展开讲解一下网络设备的注册,具体的某种网络设备的初始化我们放到linux驱动中讲解。 //-------------------------Board.c (uboot1.1.16_256m-for36---v1.01\lib_arm)----------------------------- #if (CONFIG_COMMANDS & CFG_CMD_NET) #if defined(CONFIG_NET_MULTI) puts ("Net: "); #endif eth_initialize(gd->bd); #endif //-----------------------------Eth.c (uboot1.1.16_256m-for36---v1.01\net)---------------------------------- int eth_initialize(bd_t *bis)// { char enetvar[32], env_enetaddr[6]; int i, eth_number = 0; char *tmp, *end; eth_devices = NULL; eth_current = NULL; #if defined(CONFIG_MII) || (CONFIG_COMMANDS & CFG_CMD_MII) miiphy_init(); #endif #ifdef CONFIG_INCA_IP_SWITCH inca_switch_initialize(bis); #endif #ifdef CONFIG_PLB2800_ETHER plb2800_eth_initialize(bis); #endif #ifdef SCC_ENET scc_initialize(bis); #endif #if defined(CONFIG_SK98) skge_initialize(bis); #endif #if defined(CONFIG_MPC85XX_TSEC1) tsec_initialize(bis, 0, CONFIG_MPC85XX_TSEC1_NAME); #elif defined(CONFIG_MPC83XX_TSEC1) tsec_initialize(bis, 0, CONFIG_MPC83XX_TSEC1_NAME); #endif #if defined(CONFIG_UEC_ETH1) uec_initialize(0); #endif #if defined(CONFIG_MPC86XX_TSEC1) tsec_initialize(bis, 0, CONFIG_MPC86XX_TSEC1_NAME); #endif #if defined(FEC_ENET) || defined(CONFIG_ETHER_ON_FCC) fec_initialize(bis); #endif #if defined(CONFIG_AU1X00) au1x00_enet_initialize(bis); #endif #if defined(CONFIG_IXP4XX_NPE) npe_initialize(bis); #endif #ifdef CONFIG_E1000 e1000_initialize(bis); #endif #ifdef CONFIG_EEPRO100 eepro100_initialize(bis); #endif #ifdef CONFIG_TULIP dc21x4x_initialize(bis); #endif #ifdef CONFIG_PCNET pcnet_initialize(bis); #endif #ifdef CONFIG_NATSEMI natsemi_initialize(bis); #endif #ifdef CONFIG_NS8382X ns8382x_initialize(bis); #endif #if defined(CONFIG_RTL8139) rtl8139_initialize(bis); #endif #if defined(CONFIG_RTL8169) rtl8169_initialize(bis); #endif if (!eth_devices) { //如果初始化了所有的设备后,仍没有设备注册,那么打印相关消息 puts ("No ethernet found.\n"); } else { struct eth_device *dev = eth_devices; char *ethprime = getenv ("ethprime"); //(1) “ethaddr”表示网络地址,“eth1addr”、“eth2addr”表示第2个和第3个的网络/地址; //(2) "ethprime"表示上电后初始eth_current的名称; do { if (eth_number) //网络设备序号 puts (", "); printf("%s", dev->name); //网络设备名 if (ethprime && strcmp (dev->name, ethprime) == 0) { eth_current = dev; //如果匹配到上电初始后的eth_current名,则赋值eth_current puts (" [PRIME]"); } sprintf(enetvar, eth_number ? "eth%daddr" : "ethaddr", eth_number); tmp = getenv (enetvar); //输出当前设备的物理地址 for (i=0; i<6; i++) { //获取物理地址 env_enetaddr[i] = tmp ? simple_strtoul(tmp, &end, 16) : 0; if (tmp) tmp = (*end) ? end+1 : end; } //打印物理地址信息 if (memcmp(env_enetaddr, "\0\0\0\0\0\0", 6)) { if (memcmp(dev->enetaddr, "\0\0\0\0\0\0", 6) && memcmp(dev->enetaddr, env_enetaddr, 6)) { printf ("\nWarning: %s MAC addresses don't match:\n", dev->name); printf ("Address in SROM is " "%02X:%02X:%02X:%02X:%02X:%02X\n", dev->enetaddr[0], dev->enetaddr[1], dev->enetaddr[2], dev->enetaddr[3], dev->enetaddr[4], dev->enetaddr[5]); printf ("Address in environment is " "%02X:%02X:%02X:%02X:%02X:%02X\n", env_enetaddr[0], env_enetaddr[1], env_enetaddr[2], env_enetaddr[3], env_enetaddr[4], env_enetaddr[5]); } memcpy(dev->enetaddr, env_enetaddr, 6); } eth_number++; //网络设备序号加1 dev = dev->next; //继续显示下一个网络设备的信息 } while(dev != eth_devices); #ifdef CONFIG_NET_MULTI /* update current ethernet name */ if (eth_current) {//如果当前网络设备中有激活的网络设备,则更新环境变量ethact char *act = getenv("ethact"); if (act == NULL || strcmp(act, eth_current->name) != 0) setenv("ethact", eth_current->name); } else setenv("ethact", NULL); //如果没有激活当前网络设备,则清除环境变量ethact #endif putc ('\n'); } return eth_number; } //---------------------------Rtl8169.c (uboot1.1.16_256m-for36---v1.01\drivers)-------------------------- int rtl8169_initialize(bd_t *bis) { pci_dev_t devno; int card_number = 0; struct eth_device *dev; u32 iobase; int idx=0; while(1){ /* Find RTL8169 */ if ((devno = pci_find_devices(supported, idx++)) < 0) //从支持列表中查找当前PCI设备 break; pci_read_config_dword(devno, PCI_BASE_ADDRESS_1, &iobase); iobase &= ~0xf; debug ("rtl8169: REALTEK RTL8169 @0x%x\n", iobase); dev = (struct eth_device *)malloc(sizeof *dev); sprintf (dev->name, "RTL8169#%d", card_number); dev->priv = (void *) devno; //将设备绑定到网络设备结构体,并绑定底层函数 dev->iobase = (int)bus_to_phys(iobase); dev->init = rtl_reset; dev->halt = rtl_halt; dev->send = rtl_send; dev->recv = rtl_recv; eth_register (dev); //网络设备注册 rtl_init(dev, bis); //初始化该网络设备 card_number++; } return card_number; } //------------------------------Eth.c (uboot1.1.16_256m-for36---v1.01\net)--------------------------------- static struct eth_device *eth_devices, *eth_current; int eth_register(struct eth_device* dev) { struct eth_device *d; if (!eth_devices) { eth_current = eth_devices = dev; //如果网络设备列表中还没有一个网络设备 #ifdef CONFIG_NET_MULTI //初始化网络设备列表 /* update current ethernet name */ { char *act = getenv("ethact"); //根据当前网络设备名设置环境变量ethact, "ethact"表示eth_current网络地址的名称,//eth_current改变后重新写入此环境变量; if (act == NULL || strcmp(act, eth_current->name) != 0) setenv("ethact", eth_current->name); } #endif } else { //如果已有网络设备,则在末尾插入一个网络设备 for (d=eth_devices; d->next!=eth_devices; d=d->next); d->next = dev; } dev->state = ETH_STATE_INIT; //设置该网络设备的状态为已经初始化 dev->next = eth_devices; //该设备下一个设备指向链表头,形成环链表 return 0; } 23.到这里我们可以说是进入了uboot的第三阶段了,也就是uboot解析命令执行相关操作的部分。 //-------------------------Board.c (uboot1.1.16_256m-for36---v1.01\lib_arm)----------------------------- /* main_loop() can return to retry autoboot, if so just run it again. */ for (;;) { main_loop (); } 第三章 Uboot1.1.16中的命令行解析部分 进入到main_loop,也就进入到了uboot的最后一个部分了,这个函数的实现是在Main.c (uboot1.1.16_256m-for36---v1.01\common)文件中。 1.启动次数限制功能,启动次数限制可以被用户设置一个启动次数,然后保存在Flash存储器的特定位置,当到达启动次数后,U-Boot无法启动。该功能适合一些商业产品,通过配置不同的License限制用户重新启动系统。 //----------------------------Main.c (uboot1.1.16_256m-for36---v1.01\common)-------------------------- //这之前是一些宏控制一些变量的定义,这里我们先不介绍,后面用到这些变量再展开来讲 #ifdef CONFIG_BOOTCOUNT_LIMIT unsigned long bootcount = 0; unsigned long bootlimit = 0; char *bcs; char bcs_set[16]; #endif /* CONFIG_BOOTCOUNT_LIMIT */ #ifdef CONFIG_BOOTCOUNT_LIMIT bootcount = bootcount_load(); bootcount++; bootcount_store (bootcount); sprintf (bcs_set, "%lu", bootcount); setenv ("bootcount", bcs_set); bcs = getenv ("bootlimit"); bootlimit = bcs ? simple_strtoul (bcs, NULL, 10) : 0; #endif /* CONFIG_BOOTCOUNT_LIMIT */ #ifdef CONFIG_BOOTCOUNT_LIMIT if (bootlimit && (bootcount > bootlimit)) { printf ("Warning: Bootlimit (%u) exceeded. Using altbootcmd.\n", (unsigned)bootlimit); s = getenv ("altbootcmd"); } else #endif /* CONFIG_BOOTCOUNT_LIMIT */ 很明显,以上代码主要就是从存储设备(可能是flash)加载一个bootcount变量,而这个变量应该是用来记录启动次数的,由于我们本次的启动,因此我们将这个值加1,并保存到存储设备中,同时修改环境变量bcs_set。后续再判断是否超过启动次数限制,如果超过,则使用altbootcmd环境变量作为启动命令启动。 2.Modem功能。如果系统中有Modem,打开该功能可以接受其他用户通过电话网络的拨号请求。Modem功能通常供一些远程控制的系统使用。 //----------------------------Main.c (uboot1.1.16_256m-for36---v1.01\common)-------------------------- #ifdef CONFIG_MODEM_SUPPORT if (do_mdm_init) bmp = 1; /* alternate bitmap */ #endif #ifdef CONFIG_MODEM_SUPPORT debug ("DEBUG: main_loop: do_mdm_init=%d\n", do_mdm_init); if (do_mdm_init) { char *str = strdup(getenv("mdm_cmd"));//将环境变量mdm_cmd内容填入preboot中 setenv ("preboot", str); /* set or delete definition */ if (str != NULL) free (str); mdm_init(); /* wait for modem connection */ //初始化Modem并等待连接 } #endif /* CONFIG_MODEM_SUPPORT */ //---------------------------Board.c (uboot1.1.16_256m-for36---v1.01\lib_arm)--------------------------- int mdm_init (void) { char env_str[16]; char *init_str; int i; extern char console_buffer[]; extern void enable_putc(void); extern int hwflow_onoff(int); enable_putc(); /* enable serial_putc() */ #ifdef CONFIG_HWFLOW init_str = getenv("mdm_flow_control"); //根据环境变量设置,开启或关闭串口流控 if (init_str && (strcmp(init_str, "rts/cts") == 0)) hwflow_onoff (1); else hwflow_onoff(-1); #endif for (i = 1;;i++) { sprintf(env_str, "mdm_init%d", i); if ((init_str = getenv(env_str)) != NULL) { serial_puts(init_str); //发送modem连接命令 serial_puts("\n"); for(;;) { mdm_readline(console_buffer, CFG_CBSIZE); dbg("ini%d: [%s]", i, console_buffer); if ((strcmp(console_buffer, "OK") == 0) || //接收modem连接状态 (strcmp(console_buffer, "ERROR") == 0)) { dbg("ini%d: cmd done", i); break; } else /* in case we are originating call ... */ if (strncmp(console_buffer, "CONNECT", 7) == 0) { dbg("ini%d: connect", i); return 0; } } } else break; /* no init string - stop modem init */ udelay(100000); } udelay(100000); /* final stage - wait for connect */ //等待modem连接 for(;i > 1;) { /* if 'i' > 1 - wait for connection message from modem */ mdm_readline(console_buffer, CFG_CBSIZE); dbg("ini_f: [%s]", console_buffer); if (strncmp(console_buffer, "CONNECT", 7) == 0) { dbg("ini_f: connected"); return 0; } } return 0; } 3.配置版本号 #ifdef CONFIG_VERSION_VARIABLE { extern char version_string[]; setenv ("ver", version_string); /* set version variable */ } #endif /* CONFIG_VERSION_VARIABLE */ 4.初始化Hush命令解析方式,如果定义了CFG_HUSH_PARSER,命令接收和解析将采用busybox中的hush(对应hush.c)工具来实现,与uboot原始的命令解析方法相比,该工具更加智能,这里主要讲解uboot中基于hush的命令解析流程。当在配置文件中定义了CFG_HUSH_PARSER,main_loop后续就会调用parse_file_outer,进入hush去接收并解析用户命令,否则进入一个for循环,通过len = readline (CFG_PROMPT);接收用户命令,然后调用rc = run_command (lastcommand, flag);去解析和执行命令,而在parse_file_outer里面是一大堆和hush相关的机制,最终会调用到hush中的run_pipe_real(struct pipe *pi),在该函数中经过一些解析,最终会调用到对应的命令执行函数。 关于run_pipe_real的调用层级如下: Main.c (uboot1.1.16_256m-for36---v1.01\common)->void main_loop (void) 【parse_file_outer();】 Hush.c (uboot1.1.16_256m-for36---v1.01\common)->static int parse_file_outer(FILE *f) 【rcode = parse_stream_outer(&input, FLAG_PARSE_SEMICOLON);】 Hush.c (uboot1.1.16_256m-for36---v1.01\common)->int parse_stream_outer(struct in_str *inp, int flag)【code = run_list(ctx.list_head);】 Hush.c (uboot1.1.16_256m-for36---v1.01\common)->static int run_list(struct pipe *pi) 【rcode = run_list_real(pi);】 Hush.c (uboot1.1.16_256m-for36---v1.01\common)->static int run_list_real(struct pipe *pi) 【rcode = run_pipe_real(pi);】 Hush.c (uboot1.1.16_256m-for36---v1.01\common)->static int run_pipe_real(struct pipe *pi) 【rcode = (cmdtp->cmd)(cmdtp, flag,child->argc-i,&child->argv[i]);】//命令执行函数 这里面cmdtp是对应命令的结构指针,cmd就是该命令对应的执行函数指针。在uboot中,对所有的命令,都有一个cmd_tbl_t的结构体对象与之对应。 //----------------------------Main.c (uboot1.1.16_256m-for36---v1.01\common)-------------------------- #ifndef CFG_HUSH_PARSER static char lastcommand[CFG_CBSIZE] = { 0, }; int len; int rc = 1; int flag; #endif #ifdef CFG_HUSH_PARSER u_boot_hush_start (); //初始化hush信息 #endif #ifdef CFG_HUSH_PARSER parse_file_outer(); /* This point is never reached */ for (;;); #else //.......后续省略 #endif //--------------------------Hush.c (uboot1.1.16_256m-for36---v1.01\common)---------------------------- static void u_boot_hush_reloc(void)//hush信息重定位 { unsigned long addr; struct reserved_combo *r; for (r=reserved_list; r addr = (ulong) (r->literal) + gd->reloc_off; r->literal = (char *)addr; } } struct variables shell_ver = { "HUSH_VERSION", "0.01", 1, 1, 0 }; struct variables *top_vars = &shell_ver; int u_boot_hush_start(void) { if (top_vars == NULL) { top_vars = malloc(sizeof(struct variables)); top_vars->name = "HUSH_VERSION"; //设置hush的版本信息 top_vars->value = "0.01"; top_vars->next = 0; top_vars->flg_export = 0; top_vars->flg_read_only = 1; u_boot_hush_reloc(); //hush信息重定位 } return 0; } #ifndef __U_BOOT__ static int parse_file_outer(FILE *f) #else int parse_file_outer(void) #endif { int rcode; struct in_str input; #ifndef __U_BOOT__ setup_file_in_str(&input, f); #else setup_file_in_str(&input); #endif rcode = parse_stream_outer(&input, FLAG_PARSE_SEMICOLON); return rcode; } //---------------------------Command.h (uboot1.1.16_256m-for36---v1.01\include)---------------------- /* Monitor Command Table*/ struct cmd_tbl_s { char *name; /* Command Name */ int maxargs; /* maximum number of arguments */ int repeatable; /* 是否支持自动重复,即输入一次命令,后续只需回车即自动 重复执行该条命令*/ /* Implementation function */ int (*cmd)(struct cmd_tbl_s *, int, int, char *[]); char *usage; /* Usage message (short) */ #ifdef CFG_LONGHELP char *help; /* Help message (long) */ #endif #ifdef CONFIG_AUTO_COMPLETE /* do auto completion on the arguments */ //命令行自动完成功能,与Linux的shell类似 int (*complete)(int argc, char *argv[], char last_char, int maxv, char *cmdv[]); #endif }; typedef struct cmd_tbl_s cmd_tbl_t; 这里我再举一个关于uboot命令声明的例子,我列举的是nand命令的例子。 //--------------------------Command.h (uboot1.1.16_256m-for36---v1.01\include)----------------------- /* Struct_Section定义一个结构的属性,将其放在.u_boot_cmd段当中,相当于.data/.bss这些段,这样一来,凡通过U_BOOT_CMD定义的cmd_tbl_t变量会全部被放在.u_boot_cmd段当中(可以看UBOOT的链接脚本xxx.lds),具体怎么放是链接器的工作。*/ #define Struct_Section __attribute__ ((unused,section (".u_boot_cmd"))) #ifdef CFG_LONGHELP #define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) \ cmd_tbl_t __u_boot_cmd_##name Struct_Section = {#name, maxargs, rep, cmd, usage, help} #else /* no long help info */ #define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) \ cmd_tbl_t __u_boot_cmd_##name Struct_Section = {#name, maxargs, rep, cmd, usage} //------------------------Cmd_nand.c (uboot1.1.16_256m-for36---v1.01\common)----------------------- U_BOOT_CMD(nand, 5, 1, do_nand, "nand - NAND sub-system\n", "info - show available NAND devices\n" "nand device [dev] - show or set current device\n" "nand read[.jffs2] - addr off|partition size\n" "nand write[.jffs2] - addr off|partiton size - read/write `size' bytes starting\n" " at offset `off' to/from memory address `addr'\n" #ifdef CFG_NAND_YAFFS_WRITE "nand write[.yaffs[1]] - addr off|partition size - write `size' byte yaffs image\n" " starting at offset `off' from memory address `addr' (.yaffs1 for 512+16 NAND)\n" #endif "nand erase [clean] [off size] - erase `size' bytes from\n" " offset `off' (entire device if not specified)\n" "nand bad - show bad blocks\n" "nand dump[.oob] off - dump page\n" "nand scrub - really clean NAND erasing bad blocks (UNSAFE)\n" "nand markbad off - mark bad block at offset (UNSAFE)\n" "nand biterr off - make a bit error at offset (UNSAFE)\n" "nand lock [tight] [status] - bring nand to lock state or display locked pages\n" "nand unlock [offset] [size] - unlock section\n"); 很显然,这块我们通过这个声明创建了一个存放在.u_boot_cmd段的结构体对象,其对象名就是__u_boot_cmd_##name(这里就是__u_boot_cmd_nand),对应命令名就是nand,能传递给命令的最大参数个数是5,命令支持自动重复执行,命令所对应的函数是do_nand,对应"nand - NAND sub-system\n"是该命令的用法信息,后面跟的一大堆都是帮助信息。 5.设置命令行自动完成功能,该功能与Linux的shell类似,当用户输入一部分命令后,可以通过按下键盘上的Tab键补全命令的剩余部分。 //------------------------------Main.c (uboot1.1.16_256m-for36---v1.01\common)------------------------ #ifdef CONFIG_AUTO_COMPLETE install_auto_complete(); #endif //--------------------------Command.c (uboot1.1.16_256m-for36---v1.01\common)---------------------- void install_auto_complete(void) { install_auto_complete_handler("printenv", var_complete);//将var_complete绑定到cmdtp中 install_auto_complete_handler("setenv", var_complete); #if (CONFIG_COMMANDS & CFG_CMD_RUN) install_auto_complete_handler("run", var_complete); #endif } static void install_auto_complete_handler(const char *cmd, int (*complete)(int argc, char *argv[], char last_char, int maxv, char *cmdv[])) { cmd_tbl_t *cmdtp; cmdtp = find_cmd(cmd); //查找传入的命令名是否在u_boot_cmd段中存在 if (cmdtp == NULL) return; cmdtp->complete = complete; //将传入的自动complete函数传给cmdtp命令结构体的 //对应函数指针成员 } 6.简言之就是在boot之前要预先执行的命令,可以用Ctrl+C组合键跳出。 //------------------------------Main.c (uboot1.1.16_256m-for36---v1.01\common)------------------------ #ifdef CONFIG_PREBOOT if ((p = getenv ("preboot")) != NULL) { //获取preboot环境变量中存放的命令 # ifdef CONFIG_AUTOBOOT_KEYED int prev = disable_ctrlc(1); /* disable Control C checking *///关闭Ctrl+C组合检查 # endif # ifndef CFG_HUSH_PARSER run_command (p, 0);//运行preboot中的命令,这个命令可能是MODEM初始化等 # else parse_string_outer(p, FLAG_PARSE_SEMICOLON | //如果定义了宏则用hush运行 FLAG_EXIT_FROM_LOOP); # endif # ifdef CONFIG_AUTOBOOT_KEYED disable_ctrlc(prev); /* restore Control C checking */ //恢复Ctrl+C组合检查的设置 # endif } #endif /* CONFIG_PREBOOT */ 7.引导内核 //------------------------------Main.c (uboot1.1.16_256m-for36---v1.01\common)------------------------ #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0) char *s; int bootdelay; #endif #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0) s = getenv ("bootdelay"); //获取环境变量bootdelay,并将其转换成整型 bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY; debug ("### main_loop entered: bootdelay=%d\n\n", bootdelay); //打印bootdelay # ifdef CONFIG_BOOT_RETRY_TIME init_cmd_timeout (); //初始化引导超时时间 # endif /* CONFIG_BOOT_RETRY_TIME */ #ifdef CONFIG_BOOTCOUNT_LIMIT //引导次数限制 if (bootlimit && (bootcount > bootlimit)) { printf ("Warning: Bootlimit (%u) exceeded. Using altbootcmd.\n", (unsigned)bootlimit); s = getenv ("altbootcmd"); } else #endif /* CONFIG_BOOTCOUNT_LIMIT */ s = getenv ("bootcmd"); //获取启动命令的环境变量 debug ("### main_loop: bootcmd=\"%s\"\n", s ? s : " if (bootdelay >= 0 && s && !abortboot (bootdelay)) { //abortboot 中会延时bootdelay引导 //内核或中断引导 # ifdef CONFIG_AUTOBOOT_KEYED int prev = disable_ctrlc(1); /* disable Control C checking *///关闭Ctrl+C组合检查 # endif # ifndef CFG_HUSH_PARSER run_command (s, 0); //运行bootcmd中的命令,引导内核 # else parse_string_outer(s, FLAG_PARSE_SEMICOLON | //如果定义了宏则用hush解析 FLAG_EXIT_FROM_LOOP); # endif # ifdef CONFIG_AUTOBOOT_KEYED disable_ctrlc(prev); /* restore Control C checking *///恢复Ctrl+C组合检查的设置 # endif } # ifdef CONFIG_MENUKEY if (menukey == CONFIG_MENUKEY) { s = getenv("menucmd"); //获取menucmd环境变量中的命令 if (s) { # ifndef CFG_HUSH_PARSER run_command (s, 0); //运行menucmd中的命令 # else parse_string_outer(s, FLAG_PARSE_SEMICOLON | //如果定义了宏则用hush运行 FLAG_EXIT_FROM_LOOP); # endif } } #endif /* CONFIG_MENUKEY */ #endif /* CONFIG_BOOTDELAY */ #ifdef CONFIG_BOOT_RETRY_TIME /************************** initialize command line timeout**************************/ void init_cmd_timeout(void) { char *s = getenv ("bootretry"); //获取引导重试时间的环境变量 if (s != NULL) retry_time = (int)simple_strtol(s, NULL, 10);//如果有这个环境变量,那么转换成整型 else retry_time = CONFIG_BOOT_RETRY_TIME; //如果没有,从宏获取该时间 if (retry_time >= 0 && retry_time < CONFIG_BOOT_RETRY_MIN) retry_time = CONFIG_BOOT_RETRY_MIN; //该时间不得小于最小时间限制 } static __inline__ int abortboot(int bootdelay)//延时或中断内核引导 { int abort = 0; //默认不中断内核引导 #ifdef CONFIG_SILENT_CONSOLE if (gd->flags & GD_FLG_SILENT) { /* Restore serial console */ console_assign (stdout, "serial"); //如果沉默了控制台,则设置串口作为控制台 console_assign (stderr, "serial"); } #endif #ifdef CONFIG_MENUPROMPT printf(CONFIG_MENUPROMPT, bootdelay); #else printf("Hit any key to stop autoboot: %2d ", bootdelay); //打印延时信息 #endif #if defined CONFIG_ZERO_BOOTDELAY_CHECK /* * Check if key already pressed * Don't check if bootdelay < 0 */ if (bootdelay >= 0) { if (tstc()) { /* we got a key press */ //查询串口接收buffer标志位 //tstc() => return uart->UTRSTAT & 0x1; (void) getc(); /* consume input */ //获取输入数据 puts ("\b\b\b 0"); abort = 1; /* don't auto boot */ //中断内核引导 } } #endif while ((bootdelay > 0) && (!abort)) { //bootdelay倒计时引导内核 int i; --bootdelay; //每秒减1 for (i=0; !abort && i<100; ++i) { /* delay 100 * 10ms */ if (tstc()) { /* we got a key press */ //查询串口接收buffer标志位 abort = 1; /* don't auto boot */ //中断内核引导 bootdelay = 0; /* no more delay */ //不再延时等待 # ifdef CONFIG_MENUKEY menukey = getc(); //如果使能了宏,则获取输入的字符,并赋给menukey # else (void) getc(); /* consume input */ //获取输入的字符 # endif break; } udelay (10000); //延时10ms } printf ("\b\b\b%2d ", bootdelay); //打印倒计时 } putc ('\n'); #ifdef CONFIG_SILENT_CONSOLE if (abort) { /* permanently enable normal console output */ gd->flags &= ~(GD_FLG_SILENT); //清除沉默控制台标志位 } else if (gd->flags & GD_FLG_SILENT) { /* Restore silent console */ console_assign (stdout, "nulldev"); //如果之前是沉默控制台,则去除输入输出 console_assign (stderr, "nulldev"); } #endif return abort; } //---------------------------Console.c (uboot1.1.16_256m-for36---v1.01\common)------------------------ /** U-Boot INIT FUNCTIONS *************************************************/ int console_assign (int file, char *devname)//分配控制台 { int flag, i; /* Check for valid file */ switch (file) { case stdin: flag = DEV_FLAGS_INPUT; break; case stdout: case stderr: flag = DEV_FLAGS_OUTPUT; break; default: return -1; } /* Check for valid device name */ for (i = 1; i <= ListNumItems (devlist); i++) { //从之前注册的设备列表中找到 //当前控制台设备,并设置为当前输入输出 device_t *dev = ListGetPtrToItem (devlist, i); if (strcmp (devname, dev->name) == 0) { if (dev->flags & flag) return console_setfile (file, dev); return -1; } } return -1; } static int console_setfile (int file, device_t * dev)//设置当前控制台设备,使其绑定到设备文件 { int error = 0; if (dev == NULL) return -1; switch (file) { case stdin: case stdout: case stderr: /* Start new device */ if (dev->start) { error = dev->start (); /* If it's not started dont use it */ if (error < 0) break; } /* Assign the new device (leaving the existing one started) */ stdio_devices[file] = dev; //当前设备绑定到标准输入输出的设备文件 /* * Update monitor functions * (to use the console stuff by other applications) */ switch (file) { case stdin: gd->jt[XF_getc] = dev->getc; //把当前控制台设备输入输出函数绑定到跳转表 gd->jt[XF_tstc] = dev->tstc; break; case stdout: gd->jt[XF_putc] = dev->putc; gd->jt[XF_puts] = dev->puts; gd->jt[XF_printf] = printf; break; } break; default: /* Invalid file ID */ error = -1; } return error; } //---------------------------Main.c (uboot1.1.16_256m-for36---v1.01\common)--------------------------- /**************************************************************************** * returns: * 1 - command executed, repeatable * 0 - command executed but not repeatable, interrupted commands are * always considered not repeatable * -1 - not executed (unrecognized, bootd recursion or too many args) * (If cmd is NULL or "" or longer than CFG_CBSIZE-1 it is * considered unrecognized) * * WARNING: * * We must create a temporary copy of the command since the command we get * may be the result from getenv(), which returns a pointer directly to * the environment data, which may change magicly when the command we run * creates or modifies environment variables (like "bootp" does). */ int run_command (const char *cmd, int flag) { cmd_tbl_t *cmdtp; char cmdbuf[CFG_CBSIZE]; /* working copy of cmd */ char *token; /* start of token in cmdbuf */ char *sep; /* end of token (separator) in cmdbuf */ char finaltoken[CFG_CBSIZE]; char *str = cmdbuf; char *argv[CFG_MAXARGS + 1]; /* NULL terminated */ int argc, inquotes; int repeatable = 1; int rc = 0; //------------------------------------------------------------------------------------------------------------------- c语言中 %p的含义 博文地址:http://www.cnblogs.com/myblesh/archive/2012/04/09/2439496.html 格式控制符“%p”中的p是pointer(指针)的缩写。指针的值是语言实现(编译程序)相关的,但几乎所有实现中,指针的值都是一个表示地址空间中某个存储器单元的整数。printf函数族中对于%p一般以十六进制整数方式输出指针的值,附加前缀0x。 //--------------------------------------------------------------------------------- #ifdef DEBUG_PARSER //打印命令指针地址 printf ("[RUN_COMMAND] cmd[%p]=\"", cmd); puts (cmd ? cmd : "NULL"); /* use puts - string may be loooong */ puts ("\"\n"); #endif clear_ctrlc(); //在运行命令前,清除Ctrl+C按下标志 if (!cmd || !*cmd) { //空命令检查 return -1; /* empty command */ } if (strlen(cmd) >= CFG_CBSIZE) { //命令内容过大 puts ("## Command too long!\n"); return -1; } strcpy (cmdbuf, cmd); //命令cmd的内容拷贝到str中 /* Process separators and check for invalid * repeatable commands */ #ifdef DEBUG_PARSER printf ("[PROCESS_SEPARATORS] %s\n", cmd); //打印命令信息 #endif while (*str) { //解析命令 /* * Find separator, or string end * Allow simple escape of ';' by writing "\;" */ for (inquotes = 0, sep = str; *sep; sep++) { //分离取出第一条有效命令 if ((*sep=='\'') && (*(sep-1) != '\\')) inquotes=!inquotes; if (!inquotes && (*sep == ';') && /* separator */ ( sep != str) && /* past string start */ (*(sep-1) != '\\')) /* and NOT escaped */ break; } /* * Limit the token to data between separators */ token = str; //获取str首地址 if (*sep) { str = sep + 1; /* start of command for next pass */ //跳至下一条命令 *sep = '\0'; //第一条命令结束处分隔符替换成'\0' } else str = sep; /* no more commands for next pass */ //命令行中仅有一条命令 #ifdef DEBUG_PARSER printf ("token: \"%s\"\n", token); #endif /* find macros in this token and replace them */ process_macros (token, finaltoken);//处理宏,例如当我们用网卡或者usb下载文件到 //内存中时候会临时生成一些环境变量或者宏 /* Extract arguments */ //提取单条命令中的参数 if ((argc = parse_line (finaltoken, argv)) == 0) { rc = -1; /* no command at all */ continue; } /* Look up command in command table */ if ((cmdtp = find_cmd(argv[0])) == NULL) {//根据第一个参数查找对应的命令名 printf ("Unknown command '%s' - try 'help'\n", argv[0]); rc = -1; /* give up after bad command */ continue; } /* found - check max args */ if (argc > cmdtp->maxargs) { //判断是否超过命令的最大参数个数 printf ("Usage:\n%s\n", cmdtp->usage); rc = -1; continue; } #if (CONFIG_COMMANDS & CFG_CMD_BOOTD) if (cmdtp->cmd == do_bootd) { /* 防止bootd命令的递归调用 */ #ifdef DEBUG_PARSER printf ("[%s]\n", finaltoken); #endif if (flag & CMD_FLAG_BOOTD) { puts ("'bootd' recursion detected\n"); rc = -1; continue; } else { flag |= CMD_FLAG_BOOTD; } } #endif /* CFG_CMD_BOOTD */ /* OK - call function to do the command */ if ((cmdtp->cmd) (cmdtp, flag, argc, argv) != 0) { //调用对应函数指针,执行命令 //行中的指定命令 rc = -1; } repeatable &= cmdtp->repeatable; //获取回车重复执行标识 /* Did the user stop this? */ if (had_ctrlc ()) //是否使用Ctrl+C强制结束 return 0; /* if stopped then not repeatable */ } return rc ? rc : repeatable; } 8.输出视频标识,这里我们用不到 #ifdef CONFIG_AMIGAONEG3SE { extern void video_banner(void); video_banner(); } #endif 9.uboot命令解析 #ifdef CFG_HUSH_PARSER parse_file_outer(); //如果定义了CFG_HUSH_PARSER,使用Hush解析命令 /* This point is never reached */ for (;;); //如果Hush正常解析命令,是到不了这里的,这里只是防止程序跑飞 #else for (;;) { #ifdef CONFIG_BOOT_RETRY_TIME if (rc >= 0) { /* Saw enough of a valid command to * restart the timeout. */ reset_cmd_timeout();//设置命令处理超时时间 } #endif len = readline (CFG_PROMPT); //#define CFG_PROMPT "SMDK6410 # " flag = 0; /* assume no special flags for now */ if (len > 0) strcpy (lastcommand, console_buffer); else if (len == 0) flag |= CMD_FLAG_REPEAT; #ifdef CONFIG_BOOT_RETRY_TIME else if (len == -2) { /* -2 means timed out, retry autoboot */ puts ("\nTimed out waiting for command\n"); # ifdef CONFIG_RESET_TO_RETRY /* Reinit board to run initialization code again */ do_reset (NULL, 0, 0, NULL); # else return; /* retry autoboot */ # endif } #endif if (len == -1) puts (" else rc = run_command (lastcommand, flag); if (rc <= 0) { /* invalid command or not repeatable, forget it */ lastcommand[0] = 0; } } #endif /*CFG_HUSH_PARSER*/ /* * Prompt for input and read a line. * If CONFIG_BOOT_RETRY_TIME is defined and retry_time >= 0, * time out when time goes past endtime (timebase time in ticks). * Return: number of read characters * -1 if break * -2 if timed out */ int readline (const char *const prompt) { #ifdef CONFIG_CMDLINE_EDITING char *p = console_buffer; unsigned int len=MAX_CMDBUF_SIZE; int rc; static int initted = 0; if (!initted) { hist_init(); //初始化hist initted = 1; } puts (prompt); //这里打印"SMDK6410 # "内容 rc = cread_line(p, &len); return rc < 0 ? rc : len; #else char *p = console_buffer; int n = 0; /* buffer index */ int plen = 0; /* prompt length */ int col; /* output column cnt */ char c; /* print prompt */ if (prompt) { plen = strlen (prompt); puts (prompt); } col = plen; for (;;) { #ifdef CONFIG_BOOT_RETRY_TIME while (!tstc()) { /* while no incoming data */ if (retry_time >= 0 && get_ticks() > endtime) return (-2); /* timed out */ } #endif WATCHDOG_RESET(); /* Trigger watchdog, if needed */ #ifdef CONFIG_SHOW_ACTIVITY while (!tstc()) { extern void show_activity(int arg); show_activity(0); } #endif c = getc(); /* * Special character handling */ switch (c) { case '\r': /* Enter */ case '\n': *p = '\0'; puts ("\r\n"); return (p - console_buffer); case '\0': /* nul */ continue; case 0x03: /* ^C - break */ console_buffer[0] = '\0'; /* discard input */ return (-1); case 0x15: /* ^U - erase line */ while (col > plen) { puts (erase_seq); --col; } p = console_buffer; n = 0; continue; case 0x17: /* ^W - erase word */ p=delete_char(console_buffer, p, &col, &n, plen); while ((n > 0) && (*p != ' ')) { p=delete_char(console_buffer, p, &col, &n, plen); } continue; case 0x08: /* ^H - backspace */ case 0x7F: /* DEL - backspace */ p=delete_char(console_buffer, p, &col, &n, plen); continue; default: /* * Must be a normal character then */ if (n < CFG_CBSIZE-2) { if (c == '\t') { /* expand TABs */ #ifdef CONFIG_AUTO_COMPLETE /* if auto completion triggered just continue */ *p = '\0'; if (cmd_auto_complete(prompt, console_buffer, &n, &col)) { p = console_buffer + n; /* reset */ continue; } #endif puts (tab_seq+(col&07)); col += 8 - (col&07); } else { ++col; /* echo input */ putc (c); } *p++ = c; ++n; } else { /* Buffer full */ putc ('\a'); } } } #endif /* CONFIG_CMDLINE_EDITING */ } 第四章 Uboot1.1.16如何通过启动命令启动内核 1.在调用do_bootm_linux前我们做了什么事情? 从前面我们也知道,我们内核启动就是运行了bootcmd环境变量中的命令,而在Smdk6410.c (uboot1.1.16_256m-for36---v1.01\board\samsung\smdk6410)的board_late_init中,我们可以通过幻数magic使能设置这个环境变量的值: sprintf(boot_cmd, "nand erase 0 40000;nand write %08x 0 40000", PHYS_SDRAM_1 + 0x8000); 由此我们有命令nand erase 0 40000;nand write 0x50008000 0 40000 这里,我们并没有去设置这个值,而是获取默认的环境变量default_environment //---------------------Env_common.c (uboot1.1.16_256m-for36---v1.01\common)----------------------- uchar default_environment[] = { //...... #ifdef CONFIG_BOOTCOMMAND "bootcmd=" CONFIG_BOOTCOMMAND "\0" #endif //...... "\0" }; //-----------------------Env_common.c (uboot1.1.16_256m-for36---v1.01\common)--------------------- #define CONFIG_BOOTCOMMAND "nand read 0xc0008000 0x100000 0x500000;bootm 0xc0008000" #elif defined(CONFIG_BOOT_MOVINAND) #define CFG_ENV_IS_IN_MOVINAND #define CONFIG_BOOTCOMMAND "movi read kernel c0008000;movi read rootfs c0800000;bootm c0008000" #elif defined(CONFIG_BOOT_ONENAND) || defined(CONFIG_BOOT_ONENAND_IROM) #define CFG_ONENAND_BASE (0x70100000) #define CFG_MAX_ONENAND_DEVICE 1 #define CFG_ENV_IS_IN_ONENAND #define CONFIG_BOOTCOMMAND "onenand read c0008000 40000 3c0000;bootm c0008000" 由于我们定义的是宏CONFIG_BOOTCOMMAND,因此我们这里boot_cmd的内容就是: nand read 0xc0008000 0x100000 0x500000;bootm 0xc0008000 关于nand命令我在这里就不展开来讲了,在linux驱动学习中,还会详细提到这部分的内容,这里我们着重分析一下bootm命令。 //----------------------Cmd_bootm.c (uboot1.1.16_256m-for36---v1.01\common)----------------------- U_BOOT_CMD( bootm, CFG_MAXARGS, 1, do_bootm, "bootm - boot application image from memory\n", "[addr [arg ...]]\n - boot application image stored in memory\n" "\tpassing arguments 'arg ...'; when booting a Linux kernel,\n" "\t'arg' can be the address of an initrd image\n" #ifdef CONFIG_OF_FLAT_TREE "\tWhen booting a Linux kernel which requires a flat device-tree\n" "\ta third argument is required which is the address of the of the\n" "\tdevice-tree blob. To boot that kernel without an initrd image,\n" "\tuse a '-' for the second argument. If you do not pass a third\n" "\ta bd_info struct will be passed instead\n" #endif ); ulong load_addr = CFG_LOAD_ADDR; // MEMORY_BASE_ADDRESS= 0x50000000 int do_bootm (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) { ulong iflag; ulong addr; ulong data, len, checksum; ulong *len_ptr = NULL; /* not to make warning. by scsuh */ uint unc_len = CFG_BOOTM_LEN; int i, verify; char *name, *s; int (*appl)(int, char *[]); image_header_t *hdr = &header; s = getenv ("verify"); //获取verify环境变量标识,判断是否需要校验内核 verify = (s && (*s == 'n')) ? 0 : 1; if (argc < 2) { //获取bootm的加载地址参数,如果没有,使用CFG_LOAD_ADDR地址 addr = load_addr; } else { addr = simple_strtoul(argv[1], NULL, 16); } #ifdef CONFIG_ZIMAGE_BOOT #define LINUX_ZIMAGE_MAGIC 0x016f2818 if (*(ulong *)(addr + 9*4) == LINUX_ZIMAGE_MAGIC) { //判断zImage的幻数 printf("Boot with zImage\n"); addr = virt_to_phys(addr); //虚拟地址到物理地址的映射 hdr->ih_os = IH_OS_LINUX; //值为5,表示系统是linux hdr->ih_ep = ntohl(addr); //强转32位跳转地址 goto after_header_check; //跳转到头部检查 } #endif SHOW_BOOT_PROGRESS (1); //显示引导进度 printf ("## Booting image at %08lx ...\n", addr); //打印镜像加载地址 /* Copy header so we can blank CRC field for re-calculation */ #ifdef CONFIG_HAS_DATAFLASH if (addr_dataflash(addr)){ //如果内核是在数据flash上,就从数据flash上读取头部 read_dataflash(addr, sizeof(image_header_t), (char *)&header); } else #endif memmove (&header, (char *)addr, sizeof(image_header_t));//否则直接地址访问读取头部 if (ntohl(hdr->ih_magic) != IH_MAGIC) { //判断魔数是否正确 #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); //__I386__内核镜像 /* turnof verify, * fake_header() does not fake the data crc */ verify = 0; } else #endif /* __I386__ */ { #ifdef CONFIG_IMAGE_BOOT //配置了该宏可以无视魔数的正确性 printf("Boot with Image\n"); //打印引导内核的信息 addr = virt_to_phys(addr); //加载地址从虚拟地址转换到物理地址 hdr->ih_os = IH_OS_LINUX; //设置OS类型为linux hdr->ih_ep = ntohl(addr); //设置内核入口地址 hdr->ih_comp = IH_COMP_NONE; //Compression Type 压缩类型为非压缩 goto after_header_check; //跳转到头部检查 #endif puts ("Bad Magic Number\n"); //魔数不符合uboot格式 //打印错误信息(无效的魔数) SHOW_BOOT_PROGRESS (-1); return 1; } } SHOW_BOOT_PROGRESS (2); //引导进程信息 data = (ulong)&header; len = sizeof(image_header_t); checksum = ntohl(hdr->ih_hcrc); //获取 Image Header CRC Checksum hdr->ih_hcrc = 0; if (crc32 (0, (uchar *)data, len) != checksum) { //image_header_t头部内容crc校验 puts ("Bad Header Checksum\n"); //打印错误信息 SHOW_BOOT_PROGRESS (-2); return 1; } SHOW_BOOT_PROGRESS (3); //引导进程信息 #ifdef CONFIG_HAS_DATAFLASH if (addr_dataflash(addr)){ //是否数据flash,hdr->ih_size=Image Data Size len = ntohl(hdr->ih_size) + sizeof(image_header_t); //从数据flash读取整个内核 read_dataflash(addr, len, (char *)CFG_LOAD_ADDR);//读到CFG_LOAD_ADDR addr = CFG_LOAD_ADDR; } #endif /* for multi-file images we need the data part, too */ print_image_hdr ((image_header_t *)addr); //打印内核镜像头的信息 data = addr + sizeof(image_header_t); //获取实际内核存放的首地址(去除头部) len = ntohl(hdr->ih_size); //获取实际内核的大小(去除头部) if (verify) { puts (" Verifying Checksum ... "); //校验整个内核 if (crc32 (0, (uchar *)data, len) != ntohl(hdr->ih_dcrc)) { printf ("Bad Data CRC\n"); SHOW_BOOT_PROGRESS (-3); return 1; } puts ("OK\n"); } SHOW_BOOT_PROGRESS (4); //引导进程信息 len_ptr = (ulong *)data; //实际内核首地址赋给len_ptr #if defined(__PPC__) //根据宏定义选择性判断cpu架构 if (hdr->ih_arch != IH_CPU_PPC) #elif defined(__ARM__) if (hdr->ih_arch != IH_CPU_ARM) //我们这里选择的是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) #elif defined(__avr32__) if (hdr->ih_arch != IH_CPU_AVR32) #else # error Unknown CPU type #endif { printf ("Unsupported Architecture 0x%x\n", hdr->ih_arch); //如果不支持则报错 SHOW_BOOT_PROGRESS (-4); return 1; } SHOW_BOOT_PROGRESS (5); //引导进程信息 switch (hdr->ih_type) { //镜像类型 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; 博文地址:http://blog.sina.com.cn/s/blog_79c968b9010194nr.html MULTI_IMAGE是指一个image中包含了多个文件, 主要包括: 1)LINUX Kernel,一定是第一个文件 2)randisk,根文件系统,一定是第二个文件 3)其他的文件系统或引用程序 mkimage生成的image结构如下: 1)64Byte的image header 2)4个字节,表示第一个文件的长度,kernel 3)4个字节,表示第二个文件的长度,randisk 4)重入3),连续的4字节,表示第n个文件的长度 5)一个全0的4个字节,表示length信息结束,下一个字节,是第一个文件的开始 6)上面所述的,第一个文件的开始,begin1 7)begin1 + length1为第一个文件,后面4字节对其为begin2 8)begin2 + length2为第二个文件,后面4字节对其为begin3, 以此类推 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 command\n", cmdtp->name); //错误的OS类型 SHOW_BOOT_PROGRESS (-5); return 1; } 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(); //关总中断 #ifdef CONFIG_AMIGAONEG3SE /* * We've possible left the caches enabled during * bios emulation, so turn them off again */ icache_disable(); //关icache invalidate_l1_instruction_cache(); flush_data_cache(); dcache_disable(); //关dcache #endif switch (hdr->ih_comp) { /* Compression Type */ 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; //最大拷贝64k(CHUNKSZ) 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: // 内核经过了GZIP压缩的,这里需要解压缩 printf (" Uncompressing %s ... ", name); if (gunzip ((void *)ntohl(hdr->ih_load), unc_len, (uchar *)data, &len) != 0) { //从data处解压缩内核到ih_load处 puts ("GUNZIP ERROR - must RESET board to recover\n"); SHOW_BOOT_PROGRESS (-6); //解压缩失败,打印信息,重启uboot do_reset (cmdtp, flag, argc, argv); } break; #ifdef CONFIG_BZIP2 case IH_COMP_BZIP2: // 内核经过了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. */ //从data处解压缩内核到ih_load处 i = BZ2_bzBuffToBuffDecompress ((char*)ntohl(hdr->ih_load), &unc_len, (char *)data, len, CFG_MALLOC_LEN < (4096 * 1024), 0); if (i != BZ_OK) { //解压缩失败,打印信息,重启uboot printf ("BUNZIP2 ERROR %d - must RESET board to recover\n", 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 %d\n", hdr->ih_comp); SHOW_BOOT_PROGRESS (-7); return 1; } puts ("OK\n"); SHOW_BOOT_PROGRESS (7); //进程引导信息 switch (hdr->ih_type) { //内核镜像解压缩后,在boot前,再做最后的一些处理 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; } 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 %d\n", hdr->ih_type); SHOW_BOOT_PROGRESS (-8); return 1; } SHOW_BOOT_PROGRESS (8); //打印进程信息 #if defined(CONFIG_ZIMAGE_BOOT) || defined(CONFIG_IMAGE_BOOT) after_header_check: #endif switch (hdr->ih_os) { //根据不同的OS类型,启动内核 default: /* handled by (original) Linux case */ case IH_OS_LINUX: #ifdef CONFIG_SILENT_CONSOLE //静默控制台 fixup_silent_linux(); #endif do_bootm_linux (cmdtp, flag, argc, argv, //启动linux内核 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); //通常是不会运行到这里的,除非内核启动失败 //如果运行到这里,就打印错误信息,并重启uboot #ifdef DEBUG puts ("\n## Control returned to monitor - resetting...\n"); do_reset (cmdtp, flag, argc, argv); #endif return 1; } //------------------------------Image.h (uboot1.1.16_256m-for36---v1.01\include)------------------------ /*all data in network byte order (aka natural aka bigendian) */ //这个uboot的头部的大小是64个字节 typedef struct image_header { //内核镜像uImage的头部 uint32_t ih_magic; /* Image Header Magic Number */ uint32_t ih_hcrc; /* Image Header CRC Checksum */ uint32_t ih_time; /* Image Creation Timestamp */ //镜像创建的时间 uint32_t ih_size; /* Image Data Size */ uint32_t ih_load; /* Data Load Address */ uint32_t ih_ep; /* Entry Point Address */ uint32_t ih_dcrc; /* Image Data CRC Checksum */ uint8_t ih_os; /* Operating System */ uint8_t ih_arch; /* CPU architecture */ uint8_t ih_type; /* Image Type */ uint8_t ih_comp; /* Compression Type */ uint8_t ih_name[IH_NMLEN]; /* Image Name */ #define IH_NMLEN 32 } image_header_t; 2.在do_bootm_linux里面我们做了什么事情? //------------------------- Armlinux.c (uboot1.1.16_256m-for36---v1.01\lib_arm)-------------------- 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; //---------------------------------------------------------------------------------------------------------------- 1、ramdisk、initrd是什么? ramdisk是一种基于内存的虚拟文件系统,通常用于放置内核的中间数据。 //------------------------------------------------------------------------------------------------------------------- ulong data; void (*theKernel)(int zero, int arch, uint params); image_header_t *hdr = &header; bd_t *bd = gd->bd; #ifdef CONFIG_CMDLINE_TAG char *commandline = getenv ("bootargs"); //获取启动参数的环境变量 #endif theKernel = (void (*)(int, int, uint))ntohl(hdr->ih_ep); //获取内核的入口地址 /* * Check if there is an initrd image */ if (argc >= 3) { //如果传递的参数大于3,表示要加载虚拟文件系统内核 SHOW_BOOT_PROGRESS (9); 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 //如果在数据flash上,则从flash读取内核头 if (addr_dataflash (addr)) { //复写掉原来的header read_dataflash (addr, sizeof (image_header_t), (char *) &header); } else #endif memcpy (&header, (char *) addr, //直接读取内核头 sizeof (image_header_t)); if (ntohl (hdr->ih_magic) != IH_MAGIC) { //检查魔数是否符合要求 printf ("Bad Magic Number\n"); SHOW_BOOT_PROGRESS (-10); do_reset (cmdtp, flag, argc, argv); } data = (ulong) & header; //虚拟文件系统内核头地址 len = sizeof (image_header_t); //虚拟文件系统内核头大小 checksum = ntohl (hdr->ih_hcrc); //虚拟文件系统的内核头crc校验值 hdr->ih_hcrc = 0; if (crc32 (0, (unsigned char *) data, len) != checksum) { printf ("Bad Header Checksum\n"); //虚拟文件系统内核头crc校验 SHOW_BOOT_PROGRESS (-11); do_reset (cmdtp, flag, argc, argv); } SHOW_BOOT_PROGRESS (10); //引导进程信息 print_image_hdr (hdr); //打印虚拟文件系统内核头信息 data = addr + sizeof (image_header_t); //获取虚拟文件系统实际内核地址 len = ntohl (hdr->ih_size); //获取虚拟文件系统实际内核大小 #ifdef CONFIG_HAS_DATAFLASH //如果在数据flash上,则将虚拟文件系统内核 //从data处读到CFG_LOAD_ADDR if (addr_dataflash (addr)) { read_dataflash (data, len, (char *) CFG_LOAD_ADDR); data = CFG_LOAD_ADDR; } #endif if (verify) { //校验整个虚拟文件系统的内核 ulong csum = 0; printf (" Verifying Checksum ... "); csum = crc32 (0, (unsigned char *) data, len); if (csum != ntohl (hdr->ih_dcrc)) { printf ("Bad Data CRC\n"); SHOW_BOOT_PROGRESS (-12); do_reset (cmdtp, flag, argc, argv); } printf ("OK\n"); } SHOW_BOOT_PROGRESS (11); //引导进程信息 if ((hdr->ih_os != IH_OS_LINUX) || //检查OS/cpu架构/镜像类型,任何一项不符则报错 (hdr->ih_arch != IH_CPU_ARM) || (hdr->ih_type != IH_TYPE_RAMDISK)) { printf ("No Linux ARM Ramdisk Image\n"); 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 */ //ramdisk镜像从data拷贝到ih_load内核加载地址 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 */ SHOW_BOOT_PROGRESS (14); len = data = 0; } #ifdef DEBUG if (!data) { printf ("No initrd\n"); } #endif if (data) { initrd_start = data; initrd_end = initrd_start + len; } else { initrd_start = 0; initrd_end = 0; } SHOW_BOOT_PROGRESS (15); 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); //起始tag标签 #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); //来源于bootargs环境变量 #endif #ifdef CONFIG_INITRD_TAG if (initrd_start && initrd_end) setup_initrd_tag (bd, initrd_start, initrd_end); #endif #if defined (CONFIG_VFD) || defined (CONFIG_LCD) setup_videolfb_tag ((gd_t *) gd); #endif setup_end_tag (bd); #endif /* we assume that the kernel is in place */ printf ("\nStarting kernel ...\n\n"); #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; //所有tag参数存放的首地址 params->hdr.tag = ATAG_CORE; //起始tag标识符 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); } #ifdef CONFIG_SERIAL_TAG void setup_serial_tag (struct tag **tmp) { struct tag *params = *tmp; struct tag_serialnr serialnr; void get_board_serial(struct tag_serialnr *serialnr); get_board_serial(&serialnr); params->hdr.tag = ATAG_SERIAL; params->hdr.size = tag_size (tag_serialnr); params->u.serialnr.low = serialnr.low; params->u.serialnr.high= serialnr.high; params = tag_next (params); *tmp = params; } #endif #ifdef CONFIG_SETUP_MEMORY_TAGS static void setup_memory_tags (bd_t *bd) { int i; for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) { params->hdr.tag = ATAG_MEM; params->hdr.size = tag_size (tag_mem32); //这两个值是在init_sequence中的dram_init中设置的 params->u.mem.start = bd->bi_dram[i].start; params->u.mem.size = bd->bi_dram[i].size; params = tag_next (params); } } #endif /* CONFIG_SETUP_MEMORY_TAGS */ //--------------------- Setup.h (uboot1.1.16_256m-for36---v1.01\include\asm-arm)、-------------------- #define tag_next(t) ((struct tag *)((u32 *)(t) + (t)->hdr.size)) #define tag_size(type) ((sizeof(struct tag_header) + sizeof(struct type)) >> 2) struct tag_header { u32 size; u32 tag; }; struct tag_core { u32 flags; /* bit 0 = read-only */ u32 pagesize; u32 rootdev; }; struct tag { struct tag_header hdr; union { struct tag_core core; struct tag_mem32 mem; struct tag_videotext videotext; struct tag_ramdisk ramdisk; struct tag_initrd initrd; struct tag_serialnr serialnr; struct tag_revision revision; struct tag_videolfb videolfb; struct tag_cmdline cmdline; /* * Acorn specific */ struct tag_acorn acorn; /* * DC21285 specific */ struct tag_memclk memclk; } u; }
示例:
int i = 1;
printf("%p",&i);
相当于
int i = 1;
printf("0x%x",&i);
对于32位的指针,输出一般会是类似0xf0001234之类的结果。
%p存在的理由除了附加前缀输出的便利性以外,如LS所说,指针的大小是不确定的,由实现决定。根据地址空间的大小,一般有16位、32位、64位的指针。尽管目前32位平台上的指针一般全是32位的,但旧的一些平台上可能有多种长度的指针(例如非标准的near、far、huge修饰的pointer)混用,无法用%x、%lx、%hx、%llx(对应int、long、short、long long)中的任意一种保证能输出所有类型的指针。引述一篇博文中的内容如下
而initrd全称为"boot loader initialized RAM disk",也就是由启动加载器所初始化的RamDisk设备,它的作用是完善内核的模块机制,让内核的初始化流程更具弹性;内核以及initrd,都由bootloader在机子启动后被加载至内存的指定位置,主要功能为按需加载模块以及按需改变根文件系统。更详细的内容,请参阅initrd的man手册,里面阐述了内核开发者对initrd制订的功能标准。命令:man initrd