Uboot1.1.16源代码完全注释笔记
来源:互联网 发布:java高级工程师课程 编辑:程序博客网 时间:2024/05/21 15:28
第一章 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<CONFIG_NR_DRAM_BANKS; 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<CONFIG_NR_DRAM_BANKS; 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<NB_DATAFLASH_AREA; 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_tpanel_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<reserved_list+NRES; 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 : "<UNDEFINED>");//打印命令信息
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。
示例:
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)中的任意一种保证能输出所有类型的指针。
//---------------------------------------------------------------------------------
#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 ("<INTERRUPT>\n");
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是一种基于内存的虚拟文件系统,通常用于放置内核的中间数据。
而initrd全称为"boot loader initialized RAM disk",也就是由启动加载器所初始化的RamDisk设备,它的作用是完善内核的模块机制,让内核的初始化流程更具弹性;内核以及initrd,都由bootloader在机子启动后被加载至内存的指定位置,主要功能为按需加载模块以及按需改变根文件系统。更详细的内容,请参阅initrd的man手册,里面阐述了内核开发者对initrd制订的功能标准。命令:man initrd
//-------------------------------------------------------------------------------------------------------------------
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;
}
- Uboot1.1.16源代码完全注释笔记
- uboot1.1.6完全注释
- linux 0.11源代码完全中文注释
- 我的OpenCV学习笔记(25):c++版本的高斯混合模型的源代码完全注释
- [分享]完全中文注释的uC/OS-II2.52源代码
- 《Linux内核完全注释》笔记(1)
- 《Linux内核完全注释》笔记(1)
- 《Linux 内核完全注释》阅读笔记
- 学习《linux内核完全注释》笔记
- uboot1.3.1移植到TQ2440笔记
- 《LINUX内核完全注释0.11》学习笔记(草稿)
- Linux学习笔记(linux 0.11完全注释)
- Buffalo 学习笔记- buffalo.js 源代码注释(一)
- Buffalo 学习笔记- buffalo.js 源代码注释(二)
- Buffalo 学习笔记- buffalo.js 源代码注释(三)
- 《Linux内核完全注释》与《Linux内核源代码情景分析》(上下册)
- linux内核完全注释---学习札记--linux内核源代码目录结构
- c++版本的高斯混合模型的源代码完全注释
- 欢迎使用CSDN-markdown编辑器
- redis的入门学习
- python爬虫爬取斗鱼网站信息(模拟翻页操作)
- C语言 穷举n位二进制数
- 『0002』
- Uboot1.1.16源代码完全注释笔记
- linux中的定时和延时任务
- Android List(集合)中的对象以某一个字段排序
- 『0003』
- PAT Basic 1002
- Hibernate_一级缓存_延迟加载_持久化
- noip2013转圈游戏
- 中缀表达式转后缀表达式
- NOIP 模板整理计划 NOIP2017 RP++(持续更新中~)