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的头部,是为了保证在重定位之前,不会访问到超过s3c64108K字节的引导存储区。如果这些值不在头部定义,编译器就会随机为这些值分配地址了。

.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文件中。

⑧接下来我们会判断复位唤醒的一个标志,如果唤醒了,就会调用INFORM0ELFIN_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中,使能了MMUUBOOT的基地址是不同的。

#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符合 //以上标准,则为10mslastdec记录分频系数

/* 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_strtoulUtils.c (uboot1.1.16_256m-for36---v1.01\tools\updater)

//simple_strtoul就是将字符串转换成数字

其实这个函数就是从环境变量default_environment中取出baudrate信息,并存放在gd_tbd_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)) { //23个扇区大小都是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_protectFlash.c (uboot1.1.16_256m-for36---v1.01\common)中,其中用于底层实现写

//保护的flash_real_protectCfi_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. Nave 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值为1CFG_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_chipnand_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_BBTMTD就直接在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_hwcontrols3c_nand_device_ready两个函数都比较简单我们不展开来讲,而后面在操作系统中我们再着重分析一下s3c_nand_scan_bbt坏块检查,因为这部分代码其实是直接从linux块设备驱动中搬过来的,比较复杂。注意了,在这些传递给函数指针的函数当中,最底层的函数有s3c_nand_hwcontrols3c_nand_device_readys3c_nand_enable_hweccs3c_nand_calculate_eccs3c_nand_correct_data,以上这些函数是直接跟片上寄存器打交道的,因此这些被赋值的函数指针,在board_nand_init被调用之后就能被正常使用了,但是对于s3c_nand_read_page_4bits3c_nand_write_page_4bits3c_nand_read_page_8bits3c_nand_write_page_8bit等函数,它们都调用了一些还没有被初始化的函数指针,这些函数指针是在nand_init_chip->nand_scan->nand_scan_ident中通过调用nand_set_defaultsnand_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-5IO口上拉下拉禁止

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-5IO口上拉下拉禁止

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;

}

//包含I2CVGA,键盘,串口,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_PARSERmain_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 */  //命令行自动完成功能,与Linuxshell类似

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.设置命令行自动完成功能,该功能与Linuxshell类似,当用户输入一部分命令后,可以通过按下键盘上的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”中的ppointer(指针)的缩写。指针的值是语言实现(编译程序)相关的,但几乎所有实现中,指针的值都是一个表示地址空间中某个存储器单元的整数。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位的,但旧的一些平台上可能有多种长度的指针(例如非标准的nearfarhuge修饰的pointer)混用,无法用%x%lx%hx%llx(对应intlongshortlong 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)){ //是否数据flashhdr->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; //最大拷贝64kCHUNKSZ

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;

//----------------------------------------------------------------------------------------------------------------

1ramdiskinitrd是什么?

ramdisk是一种基于内存的虚拟文件系统,通常用于放置内核的中间数据。
initrd全称为"boot loader initialized RAM disk",也就是由启动加载器所初始化的RamDisk设备,它的作用是完善内核的模块机制,让内核的初始化流程更具弹性;内核以及initrd,都由bootloader在机子启动后被加载至内存的指定位置,主要功能为按需加载模块以及按需改变根文件系统。更详细的内容,请参阅initrdman手册,里面阐述了内核开发者对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;

}

原创粉丝点击