uboot1.3.4启动第一阶段汇编代码详细分析

本文深入解析S5PV210处理器的启动流程,包括内存与外存的基础概念,以及U-Boot在该平台上的启动细节。重点介绍了BL1与BL2的协同工作原理,U-Boot第一阶段的关键步骤如异常向量表构建、时钟初始化、DDR初始化等,以及重定位过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

之前看了容易忘记,用了整整一天时间整理了,应该十分详细了,方便以后查看。

2020-6-6日,外面比较热,还是龟缩在屋里静静的分析代码爽!

-------------------------------------------------------------------------------------

1.内存外存相关基础

内存:
    SRAM    静态内存    特点就是容量小、价格高,优点是不需要软件初始化直接上电就能用
    DRAM    动态内存    特点就是容量大、价格低,缺点就是上电后不能直接使用,需要软件初始化后才可以使用。
单片机中:内存需求量小,而且希望开发尽量简单,适合全部用SRAM
嵌入式系统:内存需求量大,而且没有NorFlash等可启动介质
PC机:      内存需求量大,而且软件复杂,不在乎DRAM的初始化开销,适合全部用DRAM

外存:
    NorFlash:特点是容量小,价格高,优点是可以和CPU直接总线式相连,CPU上电后可以直接读取,所以一般用作启动介质。
    NandFlash(跟硬盘一样):特点是容量大,价格低,缺点是不能总线式访问,也就是说不能上电CPU直接读取,需要CPU先运行一些初始化软件,然后通过时序接口读写。

所以一般PC机都是:很小容量的BIOS(NorFlash)+ 很大容量的硬盘(类似于NandFlash)+ 大容量的DRAM
一般的单片机:      很小容量的NorFlash + 很小容量的SRAM
嵌入式系统:因为NorFlash很贵,随意现在很多嵌入式系统倾向于不用NorFlash,
            直接用:外接的大容量Nand + 外接大容量DRAM + SoC内置SRAM

---------------------------------------------------------------------------------   

2.S5PV210启动流程

S5PV210使用的启动方式是:外接的大容量Nand + 外接大容量DRAM + SoC内置SRAM
实际上210的启动还要更好玩一些,210内置了一块96KB大小的SRAM(叫iRAM),同时还有一块内置的64KB大小的NorFlash(叫iROM)。210的启动过程大致是:
第一步:CPU上电后先从内部IROM中读取预先设置的代码(BL0),执行。这一段IROM代码首先做了一些基本的初始化(CPU时钟、关看门狗···)(这一段IROM代码是三星出厂前设置的,三星也不知道我们板子上将来接的是什么样的DRAM,因此这一段IROM是不能负责初始化外接的DRAM的,因此这一段代码只能初始化SoC内部的东西);然后这一段代码会判断我们选择的启动模式(我们通过硬件跳线可以更改板子的启动模式),然后从相应的外部存储器去读取第一部分启动代码(BL1,大小为16KB)到内部SRAM。
第二步:从IRAM去运行刚上一步读取来的BL1(16KB),然后执行。BL1负责初始化NandFlash,然后将BL2读取到IRAM(剩余的80KB)然后运行
第三步:从IRAM运行BL2,BL2初始化DRAM,然后将OS读取到DRAM中,然后启动OS,启动过程结束


思路:因为启动代码的大小是不定的,有些公司可能96kb就够了,有些公司可能1MB都不够。所以刚才说的2步的启动方式不合适。三星的解决方案是:把启动代码分为2半(BL1和BL2),这两部分协同工作来完成启动。

-------------------------------------------------------------------------------------

3.我们这里分析的就是S5PV210启动流程的第二步。也就是uboot启动的第一阶段

uboot的第一阶段做了哪些工作
(1)构建异常向量表
(2)设置CPU为SVC模式
(3)关看门狗
(4)开发板供电置锁
(5)时钟初始化
(6)DDR初始化
(7)串口初始化并打印"OK"
(8)重定位
(9)建立映射表并开启MMU
(10)跳转到第二阶段

4.uboot启动的第一阶段详细代码分析,都是汇编,慎看(没半小时可能看不完)

/*
 *  armboot - Startup Code for S5PC110/ARM-Cortex CPU-core
 *
 *  Copyright (c) 2009	Samsung Electronics
 */

#include <config.h>
/*
#这个头文件config.h是在include目录下的,这个文件不是源码中本身存在的文件,而是配置过程中自动生成的文件,(详见mkconfig脚本)
# mkconfig脚本自动生成config.h处如下, 我们执行make x210_sd_config配置时,主Makefile会将$1 :=x210_sd传入mkconfig脚本。
# 所以自动生成的#include <config.h>中包含的是源文件中已存在的#include <configs/x210_sd.h>,这个目录是各种功能的宏定义,start.S中的各种宏定义可以来这里找
/* 
----begin: mkconfig脚本截取片段------------------------------------------------
# Create board specific header file
#
if [ "$APPEND" = "yes" ]	# Append to existing config file
then
	echo >> config.h
else
	> config.h		# Create new config file
fi
echo "/* Automatically generated - do not edit */" >>config.h
echo "#include <configs/$1.h>" >>config.h
----end--------------------------------------------------
*/

*/
#include <version.h>
/*
#这个头文件就是在源代码include/version.h中,这里面包含的是编译后自动生成的version_autogenerated.h文件,而这个文件又是由主Makefile 24-29行决定的,就是版本信息
----begin: 源码include/version.h------------------------------------------------
#ifndef	__VERSION_H__
#define	__VERSION_H__

#ifndef DO_DEPS_ONLY
#include "version_autogenerated.h"
#endif

#endif
----end--------------------------------------------------
----begin: 源码根目录Makefile 24-29行------------------------------------------------
VERSION = 1
PATCHLEVEL = 3
SUBLEVEL = 4
EXTRAVERSION =
U_BOOT_VERSION = $(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
VERSION_FILE = $(obj)include/version_autogenerated.h
----end--------------------------------------------------
*/
#if defined(CONFIG_ENABLE_MMU) 
/*
#这些宏都去x210_sd.h找即可
*/
#include <asm/proc/domain.h>
/*
#asm目录不是uboot中的原生目录,uboot中本来是没有这个目录的。
#mkconfig脚本中33-49行可知,asm目录是配置时创建的一个符号链接,实际指向的是就是asm-arm
/*
----begin: mkconfig脚本33-49部分代码------------------------------------------------
	cd ./include
	rm -f asm
	ln -s asm-$2 asm
----end--------------------------------------------------
----begin: 编译后的目录--------------------------------------------------
aston@ubuntu:~/project/JiuDingX210Uboot/uboot/include/asm/proc$ ls -l ../proc
lrwxrwxrwx 1 aston aston 9 May 30 20:46 ../proc -> proc-armv
aston@ubuntu:~/project/JiuDingX210Uboot/uboot/include/asm/proc$ cd ../proc-armv
aston@ubuntu:~/project/JiuDingX210Uboot/uboot/include/asm/proc-armv$ ls
domain.h  processor.h  ptrace.h  system.h
aston@ubuntu:~/project/JiuDingX210Uboot/uboot/include/asm/proc-armv$ cd ../proc
aston@ubuntu:~/project/JiuDingX210Uboot/uboot/include/asm/proc$ ls
domain.h  processor.h  ptrace.h  system.h
aston@ubuntu:~/project/JiuDingX210Uboot/uboot/include/asm/proc$ 
----end--------------------------------------------------
*/

*/
#endif
/*
总结:
从这里可以看出之前配置时创建的符号链接的作用,如果没有这些符号链接则编译时根本通不过,因为找不到头文件。(所以uboot不能在windows的共享文件夹下配置编译,因为windows中没有符号链接)
思考:为什么start.S不直接包含asm-arm/proc-armv/domain.h,而要用asm/proc/domain.h。这样的设计主要是为了可移植性。因为如果直接包含,则start
.S文件和CPU架构(和硬件)有关了,可移植性就差了。譬如我要把uboot移植到mips架构下,则start.S源代码中所有的头文件包含全部要修改。我们用了符号链接之后,则start.S中源代码不用改,只需要在具体的硬件移植时配置不同,创建的符号链接指向的不同,则可以具有可移植性。
*/
#include <regs.h>
/*
aston@ubuntu:~/project/JiuDingX210Uboot/uboot/include$ ls -l regs.h
lrwxrwxrwx 1 aston aston 9 May 30 20:46 regs.h -> s5pc110.h
符号链接指向源码include目录下s5pc110.h,主要是定义了这款soc相关的外设的地址,DMA/LCD/USB/I2C/SPI/时钟/GPIO/访问控制(tzpc)/SDRAM控制器/内存控制器等各种寄存器地址的定义
*/
>>>>>>>>>>>>>>>>>>>>>>>>>以下内容没啥太大作用,干掉也没事>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
#ifndef CONFIG_ENABLE_MMU
#ifndef CFG_PHY_UBOOT_BASE
#define CFG_PHY_UBOOT_BASE	CFG_UBOOT_BASE   
#endif
#endif
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
/*根据是否使能了内存管理单元(虚拟地址映射),来定义uboot的基地址,我们在x210_sd.h中定义了CFG_PHY_UBOOT_BASE为33e00000,相当于这块代码在uboot中无用*/
/*
 *************************************************************************
 *
 * Jump vector table as in table 3.1 in [1]
 *
 *************************************************************************
 */

#if defined(CONFIG_EVT1) && !defined(CONFIG_FUSED)
	.word 0x2000
	.word 0x0
	.word 0x0
	.word 0x0
#endif
/*
Arm-cortex-a8/arm9等体系结构中字长为32位,半字为16位,而一般的8位16位处理器的字长一般为16位。
.word的意思是在当前地址处放一个数据值,并且这个数据位数是32位的。
定义四个四字节的变量,16字节占位,这个是镜像文件前的16字节。启动代码的16字节头部
在SD卡启动/Nand启动等整个镜像开头需要16字节的校验头。(mkv210image.c中就是为了计算这个校验头)。我们以前做裸机程序时根本没考虑这16字节校验头,因为:1、如果我们是usb启动直接下载的方式启动的则不需要16字节校验头(irom application note);2、如果是SD卡启动mkv210image.c中会给原镜像前加16字节的校验头。
uboot这里start.S中在开头位置放了16字节的填充占位,这个占位的16字节只是保证正式的image的头部确实有16字节,但是这16字节的内容是不对的,还是需要后面去计算校验和然后重新填充的。在源码目录sd_fusing下的C110-EVT1-mkbl1.c中计算校验头
*/
.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      
/*
(1)异常向量表是硬件决定的,软件只是参照硬件的设计来实现它。
(2)异常向量表中每种异常都应该被处理,否则真遇到了这种异常就跑飞了。但是我们在uboot中并未非常细致的处理各种异常。
(3)复位异常处的代码是:b reset,因此在CPU复位后真正去执行的有效代码是reset处的代码,因此reset符号处才是真正的有意义的代码开始的地方。

.balignl 16,0xdeadbeef  
以当前地址为开始开始,找到第一次出现的以第一个参数为整数倍的地址,并将其作为结束地址,
在这个结束地址前面存储一个第二个参数长度的数据(是数字不是字符串,坏牛肉),存储内容正是第二个参数。如果当前地址正好是第一个参数的倍数,则没有数据被写入到内存。
ARM指令集是32位的,一次是4字节跳转
前15句共用了4*15=60字节的存储单元(ARM指令集是32位的,一次是4字节跳转),开始执第16句时pc指向地址56(0x00000038)处,开始执行第18行时pc指向60,这时编辑代码的人刚好想在这个地址开始的存储空间处存放一个特殊标记值0xdeadbeef(占用4字节),存放完之后pc指针要指向64才对是吧,很好,64刚好是16的整数倍,就用16
为什么要对齐访问?有时候是效率的要求,有时候是硬件的特殊要求。
*/
/*
 *************************************************************************
 *
 * Startup Code (reset vector)
 *
 * do important init only if we don't start from memory!
 * setup Memory and board specific bits prior to relocation.
 * relocate armboot to ram
 * setup stack
 *
 *************************************************************************
 */

_TEXT_BASE:
	.word	TEXT_BASE
/*
Makefile中配置阶段制定的链接时uboot的链接地址
----begin: Makefile中--------------------------------------------------
x210_sd_config :	unconfig
	@$(MKCONFIG) $(@:_config=) arm s5pc11x x210 samsung s5pc110
	@echo "TEXT_BASE = 0xc3e00000" > $(obj)board/samsung/x210/config.mk
----end--------------------------------------------------
 */
/*
 * Below variable is very important because we use MMU in U-Boot.
 * Without it, we cannot run code correctly before MMU is ON.
 * by scsuh.
 */
_TEXT_PHY_BASE:
	.word	CFG_PHY_UBOOT_BASE
/*
33e00000	uboot在DDR中的物理地址
*/
.globl _armboot_start
_armboot_start:
	.word _start
/*重定位的时候用这个*/
/*
 * These are defined in the board-specific linker script.
 */
.globl _bss_start
_bss_start:
	.word __bss_start

.globl _bss_end
_bss_end:
	.word _end
/*清bss段可以用到*/

>>>>>>>>>>>>>>>>>>>>在uboot中一般也是不用中断的,可以删掉>>>>>>>
#if defined(CONFIG_USE_IRQ)
/* IRQ stack memory (calculated at run-time) */
.globl IRQ_STACK_START
IRQ_STACK_START:
	.word	0x0badc0de

/* IRQ stack memory (calculated at run-time) */
.globl FIQ_STACK_START
FIQ_STACK_START:
	.word 0x0badc0de
#endif
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

/*
 * the actual reset code
 */

reset:
	/*
	 * set the cpu to SVC32 mode and IRQ & FIQ disable
	 */
	msr	cpsr_c, #0xd3		@ I & F disable, Mode: 0x13 - SVC   
/*
将CPU设置为禁止FIQ IRQ,ARM状态,SVC模式。其实ARM CPU在复位时默认就会进入SVC模式,但是这里还是使用软件将其置为SVC模式。整个uboot工作时CPU一直处于SVC模式。避免硬件没将其预制svc模式
整个uboot工作时,cpu一直处于特权模式,方便处理各种事情
*/


/*
 *************************************************************************
 *
 * CPU_init_critical registers
 *
 * setup important registers
 * setup memory timing
 *
 *************************************************************************
 */
         /*
         * we do sys-critical inits only at reboot,
         * not when booting from ram!
         */
cpu_init_crit:
>>>>>>>>>>>>>>>>>>>>>>>>>代码中定义了CONFIG_EVT1,下面这部分可以删掉>>>>>>>>>
	#ifndef CONFIG_EVT1
#if 0	
	bl	v7_flush_dcache_all
#else
	bl	disable_l2cache

	mov	r0, #0x0	@ 
	mov	r1, #0x0	@ i	
	mov	r3, #0x0
	mov	r4, #0x0
lp1:
	mov	r2, #0x0	@ j
lp2:	
	mov	r3, r1, LSL #29		@ r3 = r1(i) <<29
	mov	r4, r2, LSL #6		@ r4 = r2(j) <<6
	orr	r4, r4, #0x2		@ r3 = (i<<29)|(j<<6)|(1<<1)
	orr	r3, r3, r4
	mov	r0, r3			@ r0 = r3
	bl	CoInvalidateDCacheIndex
	add	r2, #0x1		@ r2(j)++
	cmp	r2, #1024		@ r2 < 1024
	bne	lp2			@ jump to lp2
	add	r1, #0x1		@ r1(i)++
	cmp	r1, #8			@ r1(i) < 8
	bne	lp1			@ jump to lp1

	bl	set_l2cache_auxctrl
	
	bl	enable_l2cache
#endif
#endif
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

	bl	disable_l2cache    /* 禁止L2 cache */

	bl	set_l2cache_auxctrl_cycle /* l2 cache相关初始化 */

	bl	enable_l2cache /* 使能l2 cache */
	
       /*
        * Invalidate L1 I/D 关闭L1的icache和dcache
        */
        mov	r0, #0                  @ set up for MCR
        mcr	p15, 0, r0, c8, c7, 0   @ invalidate TLBs
        mcr	p15, 0, r0, c7, c5, 0   @ invalidate icache

       /*
        * disable MMU stuff and caches   关闭MMU
        */
        mrc	p15, 0, r0, c1, c0, 0
        bic	r0, r0, #0x00002000     @ clear bits 13 (--V-)
        bic	r0, r0, #0x00000007     @ clear bits 2:0 (-CAM)
        orr	r0, r0, #0x00000002     @ set bit 1 (--A-) Align
        orr	r0, r0, #0x00000800     @ set bit 12 (Z---) BTB
        mcr 	p15, 0, r0, c1, c0, 0

/*上面5部都是CPU相关的,一般不要重点关注*/
        /* Read booting information */ 
        ldr	r0, =PRO_ID_BASE   /*E0000000*/
        ldr	r1, [r0,#OMR_OFFSET]/*将 (E0000000+4)地址放入r1,CPU手册中没查到这个地址*/
        bic	r2, r1, #0xffffffc1  /*r2中的数值决定从外部哪里启动 sd启动/inand启动*/
/*
识别并暂存启动介质选择
(1)从哪里启动是由SoC的OM5:OM0这6个引脚的高低电平决定的。
(2)实际上在210内部有一个寄存器(地址是0xE0000004),这个寄存器中的值是硬件根据OM引脚的设置而自动设置值的。这个值反映的就是OM引脚的接法(电平高低),也就是真正的启动介质是谁。
(3)我们代码中可以通过读取这个寄存器的值然后判断其值来确定当前选中的启动介质是Nand还是SD还是其他的。
*/
>>>>>>>>>>>>>>>>>>>>下面这个宏没定义,可以删掉>>>>>>>>>>>>>>>>
#ifdef CONFIG_VOGUES
	/* PS_HOLD(GPH0_0) set to output high */
	ldr	r0, =ELFIN_GPIO_BASE
	ldr	r1, =0x00000001
	str	r1, [r0, #GPH0CON_OFFSET]

	ldr	r1, =0x5500
	str	r1, [r0, #GPH0PUD_OFFSET]

	ldr	r1, =0x01
	str	r1, [r0, #GPH0DAT_OFFSET]
#endif
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>>>我们的启动方式不是NAND,下面可以删掉>>>>>>>>>>
	/* NAND BOOT */
	cmp	r2, #0x0		@ 512B 4-cycle   512B一个页大小,地址几个cycle
	moveq	r3, #BOOT_NAND

	cmp	r2, #0x2		@ 2KB 5-cycle
	moveq	r3, #BOOT_NAND

	cmp	r2, #0x4		@ 4KB 5-cycle	8-bit ECC
	moveq	r3, #BOOT_NAND

	cmp	r2, #0x6		@ 4KB 5-cycle	16-bit ECC
	moveq	r3, #BOOT_NAND

	cmp	r2, #0x8		@ OneNAND Mux
	moveq	r3, #BOOT_ONENAND
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
	/* SD/MMC BOOT */
	cmp     r2, #0xc
	moveq   r3, #BOOT_MMCSD	
/*
通过比较r2的数值,决定启动方式,我们是SD/MMC BOOT启动,用r3=0x3来表示,以备用
 */

>>>>>>>>>>>>>>>>>>>>其他启动方式处理,也不用管>>>>>>>>>>>>>>>>
	/* NOR BOOT */
	cmp     r2, #0x14
	moveq   r3, #BOOT_NOR	

	/* Uart BOOTONG failed */
	cmp     r2, #(0x1<<4)
	moveq   r3, #BOOT_SEC_DEV
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
	ldr	r0, =INF_REG_BASE
	str	r3, [r0, #INF_REG3_OFFSET]        /*这是啥?*/

	/*
	 * Go setup Memory and board specific bits prior to relocation.
	 */

	ldr	sp, =0xd0036000 /* end of sram dedicated to u-boot  这个地址是在《S5PV210_iROM_ApplictionNote》中RW / ZI region Heap段所在的地址,*/
	sub	sp, sp, #12	/* set stack */
	mov	fp, #0
/*
sp指针赋值设置栈,在sram中。因为当前整个代码还在SRAM中运行,此时DDR还未被初始化还不能用。栈地址0xd0036000是自己指定的,指定的原则就是这块空间只给栈用,不会被别人占用。
在调用函数前初始化栈,主要原因是在被调用的函数内还有再次调用函数,而BL只会将返回地址存储到LR中,但是我们只有一个LR,所以在第二层调用函数前要先将LR入栈,否则函数返回时第一层的返回地址就丢了。
*/
	bl	lowlevel_init	/* go setup pll,mux,memory */
	/* To hold max8698 output before releasing power on switch,
	 * set PS_HOLD signal to high
	 */
-------------------------\uboot\board\samsung\x210\lowlevel_init.S------------------------------------------
	.globl lowlevel_init
lowlevel_init:
	push	{lr}  /*下面会调用函数,这里将lr压栈*/

	/* check reset status  检查复位状态*/
	/*(1)复杂CPU允许多种复位情况。譬如直接冷上电、热启动、睡眠(低功耗)状态下的唤醒等,这些情况都属于复位。所以我们在复位代码中要去检测复位状态,来判断到底是哪种情况。
      (2)判断哪种复位的意义在于:冷上电时DDR是需要初始化才能用的;而热启动或者低功耗状态下的复位则不需要再次初始化DDR。*/
	
	ldr	r0, =(ELFIN_CLOCK_POWER_BASE+RST_STAT_OFFSET)
	ldr	r1, [r0]
	bic	r1, r1, #0xfff6ffff
	cmp	r1, #0x10000
	beq	wakeup_reset_pre
	cmp	r1, #0x80000
	beq	wakeup_reset_from_didle

	/* IO Retention release IO状态恢复,这个和上一个和主线启动代码都无关,因此不用去管他。*/
	ldr	r0, =(ELFIN_CLOCK_POWER_BASE + OTHERS_OFFSET)
	ldr	r1, [r0]
	ldr	r2, =IO_RET_REL
	orr	r1, r1, r2
	str	r1, [r0]

	/* Disable Watchdog  关闭看门狗*/
	ldr	r0, =ELFIN_WATCHDOG_BASE	/* 0xE2700000 */
	mov	r1, #0
	str	r1, [r0]
>>>>>>>>>>>>>>>>>>>>>>下面是对板子外接的srom,sram初始化,不管他。在手册section01 memory map可以看到内部irom和iram,还可以接外部的>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
	/* SRAM(2MB) init for SMDKC110 */
	/* GPJ1 SROM_ADDR_16to21 */
	ldr	r0, =ELFIN_GPIO_BASE
	
	ldr	r1, [r0, #GPJ1CON_OFFSET]
	bic	r1, r1, #0xFFFFFF
	ldr	r2, =0x444444
	orr	r1, r1, r2
	str	r1, [r0, #GPJ1CON_OFFSET]

	ldr	r1, [r0, #GPJ1PUD_OFFSET]
	ldr	r2, =0x3ff
	bic	r1, r1, r2
	str	r1, [r0, #GPJ1PUD_OFFSET]

	/* GPJ4 SROM_ADDR_16to21 */
	ldr	r1, [r0, #GPJ4CON_OFFSET]
	bic	r1, r1, #(0xf<<16)
	ldr	r2, =(0x4<<16)
	orr	r1, r1, r2
	str	r1, [r0, #GPJ4CON_OFFSET]

	ldr	r1, [r0, #GPJ4PUD_OFFSET]
	ldr	r2, =(0x3<<8)
	bic	r1, r1, r2
	str	r1, [r0, #GPJ4PUD_OFFSET]


	/* CS0 - 16bit sram, enable nBE, Byte base address */
	ldr	r0, =ELFIN_SROM_BASE	/* 0xE8000000 */
	mov	r1, #0x1
	str	r1, [r0]
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
	/* PS_HOLD pin(GPH0_0) set to high 开发板供电锁存*/
	ldr	r0, =(ELFIN_CLOCK_POWER_BASE + PS_HOLD_CONTROL_OFFSET)
	ldr	r1, [r0]
	orr	r1, r1, #0x300	
	orr	r1, r1, #0x1	/*两行来写是避免非法立即数??*/
	str	r1, [r0]
/*在lowlevel_init.S源码前100行,并没有做太多有意义的事情(除了关看门狗、供电锁存外),然后下面从110行才开始进行有意义的操作。*/
	/* when we already run in ram, we don't need to relocate U-Boot.
	 * and actually, memory controller must be configured before U-Boot
	 * is running in 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     1f			/* r0 == r1 then skip sdram init   1是标号,f跳转方向向后。b是backward的意思,既然有backward就有forward*/
/*判断当前代码执行位置
(1)这几行代码的作用就是判定当前代码执行的位置在SRAM中还是在DDR中。为什么要做这个判定?
原因1:BL1(uboot的前一部分)在SRAM中有一份,在DDR中也有一份,因此如果是冷启动那么当前代码应该是在SRAM中运行的BL1,如果是低功耗状态的复位这时候应该就是在DDR中运行的。
原因2:我们判定当前运行代码的地址是有用的,可以指导后面代码的运行。譬如在lowlevel_init.S中判定当前代码的运行地址,就是为了确定要不要执行时钟初始化和初始化DDR的代码。
如果当前代码是在SRAM中,说明冷启动,那么时钟和DDR都需要初始化;如果当前代码是在DDR中,那么说明是热启动则时钟和DDR都不用再次初始化。
(2)bic	r1, pc, r0	这句代码的意义是:将pc的值中的某些bit位清0,剩下一些特殊的bit位赋值给r1(r0中为1的那些位清零)相等于:r1 = pc & ~(ff000fff)
ldr	r2, _TEXT_BASE	加载链接地址到r2,然后将r2的相应位清0剩下特定位。
(3)最后比较r1和r2.
总结:这一段代码是通过读取当前运行地址和链接地址,然后处理两个地址后对比是否相等,来判定当前运行是在SRAM中(不相等)还是DDR中(相等)。从而决定是否跳过下面的时钟和DDR初始化。
*/
	/* init system clock */
	bl system_clock_init
/*
在x210_sd.h中300行到428行,都是和时钟相关的配置值。这些宏定义就决定了210的时钟配置是多少。
也就是说代码在lowlevel_init.S中都写好了,但是代码的设置值都被宏定义在x210_sd.h中了。
因此,如果移植时需要更改CPU的时钟设置,根本不需要动代码,只需要在x210_sd.h中更改配置值即可。*/

	/* Memory initialize */
	bl mem_ctrl_asm_init
/*
(1)该函数用来初始化DDR
(2)函数位置在uboot/cpu/s5pc11x/s5pc110/cpu_init.S文件中。
(3)该函数和裸机中初始化DDR代码是一样的。实际上裸机中初始化DDR的代码就是从这里抄的。配置值也可以从这里抄,但是当时我自己根据理解+抄袭整出来的一份。
(4)配置值中其他配置值参考裸机中的解释即可明白,有一个和裸机中讲的不一样。DMC0_MEMCONFIG_0,在裸机中配置值为0x20E01323;在uboot中配置为0x30F01313.这个配置不同就导致结果不同。
在 裸机中DMC0的256MB内存地址范围是0x20000000-0x2FFFFFFF;   ????
在uboot中DMC0的256MB内存地址范围为0x30000000-0x3FFFFFFF。   ????
(5)之前在裸机中时配置为2开头的地址,当时并没有说可以配置为3开头。从分析九鼎移植的uboot可以看出:DMC0上允许的地址范围是20000000-3FFFFFFF(一共是512MB),而我们实际只接了256MB物理内存,SoC允许我们给这256MB挑选地址范围。
(6)总结一下:在uboot中,可用的物理地址范围为:0x30000000-0x4FFFFFFF。一共512MB,其中30000000-3FFFFFFF为DMC0,40000000-4FFFFFFF为DMC1。
(7)我们需要的内存配置值在x210_sd.h的438行到468行之间。分析的时候要注意条件编译的条件,配置头文件中考虑了不同时钟配置下的内存配置值,这个的主要目的是让不同时钟需求的客户都能找到合适自己的内存配置值。
(8)在uboot中DMC0和DMC1都工作了,所以在裸机中只要把uboot中的配置值和配置代码全部移植过去,应该是能够让DMC0和DMC1都工作的。  
 */
1:
	/* for UART */
	bl uart_asm_init
/*
(1)这个函数用来初始化串口
(2)初始化完了后通过串口发送了一个'O'
*/
	bl tzpc_init
/*TZPC(trust zone)提供了将内存区域划分为secure和non-secure的软件接口*/
>>>>>>>>>>>>>>我们板子不是下面这两个>>>>>>
#if defined(CONFIG_ONENAND)
	bl onenandcon_init
#endif

#if defined(CONFIG_NAND)
	/* simple init for NAND */
	bl nand_asm_init
#endif
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

	/* check reset status  */
	
	ldr	r0, =(ELFIN_CLOCK_POWER_BASE+RST_STAT_OFFSET)
	ldr	r1, [r0]
	bic	r1, r1, #0xfffeffff
	cmp	r1, #0x10000
	beq	wakeup_reset_pre

	/* ABB disable */
	ldr	r0, =0xE010C300
	orr	r1, r1, #(0x1<<23)
	str	r1, [r0],做2次则第2次无意义;做代码移植时有一个古怪谨慎保守策略就是尽量添加代码而不要删除代码。

	/* Print 'K' */  
	ldr	r0, =ELFIN_UART_CONSOLE_BASE
	ldr	r1, =0x4b4b4b4b
	str	r1, [r0, #UTXH_OFFSET]
    /*返回前打印K*/
	pop	{pc}
/*lowlevel_init.S执行完如果没错那么就会串口打印出"OK"字样。这应该是我们uboot中看到的最早的输出信息。*/

lowlevel_init.S: 检查复位状态,IO恢复,关看门狗,开发板供电锁存,时钟初始化,DDR初始化,打印OK
---------------------------------------------------------------------------------------------------------
	ldr	r0, =0xE010E81C  /* PS_HOLD_CONTROL register */
	ldr	r1, =0x00005301	 /* PS_HOLD output high	*/
	str	r1, [r0]
/*又来了一遍开发板供电锁存,在lowlevel_init.S其中搞了一遍,这里再搞一遍也没啥影响,做2次则第2次无意义;做代码移植时有一个古怪谨慎保守策略就是尽量添加代码而不要删除代码。*/

	/* get ready to call C functions */
	ldr	sp, _TEXT_PHY_BASE	/* setup temp stack pointer */
	sub	sp, sp, #12
	mov	fp, #0			/* no previous frame, so fp=0 */
/*之前在调用lowlevel_init程序前设置过1次栈(源码start.S 284-287行),那时候因为DDR尚未初始化,因此程序执行都是在SRAM中,所以在SRAM中分配了一部分内存作为栈。本次因为DDR已经被初始化了,因此要把栈挪移到DDR中,所以要重新设置栈,这是第二次(源码start.S 297-299行);
这里实际设置的栈的地址是33E00000,刚好在uboot的代码段的下面紧挨着。arm从栈地址开始是满减栈,向下。 而uboot基地址开始,栈向上。不会产生冲突
(3)为什么要再次设置栈?DDR已经初始化了,已经有大片内存可以用了,没必要再把栈放在SRAM中可怜兮兮的了;原来SRAM中内存大小空间有限,栈放在那里要注意不能使用过多的栈否则栈会溢出,我们及时将栈迁移到DDR中也是为了尽可能避免栈使用时候的小心翼翼。
uboot的启动阶段主要技巧就在于小范围内有限条件下的辗转腾挪。
*/
	/* when we already run in ram, we don't need to relocate U-Boot.
	 * and actually, memory controller must be configured before U-Boot
	 * is running in 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   */
/*
(1)再次用相同的代码判断运行地址是在SRAM中还是DDR中,不过本次判断的目的不同(上次判断是为了决定是否要执行初始化时钟和DDR的代码)这次判断是为了决定是否进行uboot的relocate。
(2)冷启动时当前情况是uboot的前一部分(16kb或者8kb)开机自动从SD卡加载到SRAM中正在运行,uboot的第二部分(其实第二部分是整个uboot)还躺在SD卡的某个扇区开头的N个扇区中。此时uboot的第一阶段已经即将结束了(第一阶段该做的事基本做完了),结束之前要把第二部分加载到DDR中链接地址处(33e00000),这个加载过程就叫重定位。
*/

#if defined(CONFIG_EVT1)
	/* If BL1 was copied from SD/MMC CH2 */
	ldr	r0, =0xD0037488   /*在《S5OV210_iROM_Appli》手册中直接搜到,代表当前boot启动通道*/
	ldr	r1, [r0]
	ldr	r2, =0xEB200000
	cmp	r1, r2
	beq     mmcsd_boot
#endif
/*D0037488这个内存地址在SRAM中,这个地址中的值是被硬件自动设置的。硬件根据我们实际电路中SD卡在哪个通道中,会将这个地址中的值设置为相应的数字。譬如我们从SD0通道启动时,这个值为EB000000;从SD2通道启动时,这个值为EB200000*/
/*上面这段代码和下面这段实现的是一样的效果。两端代码冗余。最终是去mmcsd_boot执行
/*我们在start.S的260行确定了从MMCSD启动,然后又在278行将#BOOT_MMCSD写入了INF_REG3寄存器中存储着。然后又在322行读出来,再和#BOOT_MMCSD去比较,确定是从MMCSD启动。最终跳转到mmcsd_boot函数中去执行重定位动作。*/
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
	ldr	r0, =INF_REG_BASE
	ldr	r1, [r0, #INF_REG3_OFFSET]/*之前分析过,INF_REG3_OFFSET寄存器存放的是启动方式*/
	cmp	r1, #BOOT_NAND		/* 0x0 => boot device is nand */
	beq	nand_boot
	cmp	r1, #BOOT_ONENAND	/* 0x1 => boot device is onenand */
	beq	onenand_boot
	cmp     r1, #BOOT_MMCSD
	beq     mmcsd_boot
	cmp     r1, #BOOT_NOR
	beq     nor_boot
	cmp     r1, #BOOT_SEC_DEV
	beq     mmcsd_boot
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
nand_boot:
	mov	r0, #0x1000
	bl	copy_from_nand
	b	after_copy

onenand_boot:
	bl	onenand_bl2_copy
	b	after_copy

mmcsd_boot:
#if DELETE
	ldr     sp, _TEXT_PHY_BASE      
	sub     sp, sp, #12
	mov     fp, #0
#endif
	bl      movi_bl2_copy
	b       after_copy
/*
b用于不返回的跳转,比如跳到某个标号处,b  .  其中的‘.’代表当前地址,那么 b  .  就是死循环。
bl用于子程序跳转,要返回地址,返回地址存于LR中。当发生bl跳转前,会在寄存器 R14 (即LR)中保存当前PC-4,即bl跳转指令的下一条指令的地址。所以在返回时只要 MOV pc,lr 。
真正的重定位是通过调用movi_bl2_copy函数完成的,在uboot/cpu/s5pc11x/movi.c中。是一个C语言的函数
copy_bl2(2, MOVI_BL2_POS, MOVI_BL2_BLKCNT,
			CFG_PHY_UBOOT_BASE, 0);
分析参数:2表示通道2;MOVI_BL2_POS是uboot的第二部分在SD卡中的开始扇区,这个扇区数字必须和烧录uboot时烧录的位置相同;MOVI_BL2_BLKCNT是uboot的长度占用的扇区数;CFG_PHY_UBOOT_BASE是重定位时将uboot的第二部分复制到DDR中的起始地址(33E00000).
*/

nor_boot:
	bl      read_hword
	b       after_copy


after_copy:

#if defined(CONFIG_ENABLE_MMU)
enable_mmu:
	/* enable domain access */
	ldr	r5, =0x0000ffff
	mcr	p15, 0, r5, c3, c0, 0		@load domain access register
/*使能域访问(cp15的c3寄存器)
(1)cp15协处理器内部有c0到c15共16个寄存器,这些寄存器每一个都有自己的作用。我们通过mrc和mcr指令来访问这些寄存器。所谓的操作cp协处理器其实就是操作cp15的这些寄存器。
(2)c3寄存器在mmu中的作用是控制域访问。域访问是和MMU的访问控制有关的。*/
	/* 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
/*
设置TTB(cp15的c2寄存器)
(1)TTB就是translation table base,转换表基地址。TT(translation table转换表),TTB其实就是转换表的基地址。
(2)转换表是建立一套虚拟地址映射的关键。转换表分2部分,表索引和表项。表索引对应虚拟地址,表项对应物理地址。一对表索引和表项构成一个转换表单元,能够对一个内存块进行虚拟地址转换。(映射中基本规定中规定了内存映射和管理是以块为单位的,至于块有多大,要看你的MMU的支持和你自己的选择。在ARM中支持3种块大小,细表1KB、粗表4KB、段1MB)。真正的转换表就是由若干个转换表单元构成的,每个单元负责1个内存块,总体的转换表负责整个内存空间(0-4G)的映射。
(3)整个建立虚拟地址映射的主要工作就是建立这张转换表
(4)转换表放置在内存中的,放置时要求起始地址在内存中要xx位对齐。转换表不需要软件去干涉使用,而是将基地址TTB设置到cp15的c2寄存器中,然后MMU工作时会自动去查转换表。
 */
	/* Enable the MMU */
mmu_on:
	mrc	p15, 0, r0, c1, c0, 0
	orr	r0, r0, #1
	mcr	p15, 0, r0, c1, c0, 0
	nop
	nop
	nop
	nop
/*使能MMU单元(cp15的c1寄存器)
(1)cp15的c1寄存器的bit0控制MMU的开关。只要将这一个bit置1即可开启MMU。开启MMU之后上层软件层的地址就必须经过TT的转换才能发给下层物理层去执行。*/
#endif
/*找到映射表待分析
(1)通过符号查找,确定转换表在lowlevel_init.S文件的593行。*/
/*宏观上理解转换表:整个转换表可以看作是一个int类型的数组,数组中的一个元素就是一个表索引和表项的单元。数组中的元素值就是表项,这个元素的数组下标就是表索引。
ARM的段式映射中长度为1MB,因此一个映射单元只能管1MB内存,那我们整个4G范围内需要4G/1MB=4096个映射单元,也就是说这个数组的元素个数是4096.实际上我们做的时候并没有依次单个处理这4096个单元,而是把4096个分成几部分,然后每部分用for循环做相同的处理。*/
skip_hw_init:
	/* Set up the stack						    */
stack_setup:
#if defined(CONFIG_MEMORY_UPPER_CODE)
	ldr	sp, =(CFG_UBOOT_BASE + CFG_UBOOT_SIZE - 0x1000)
#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                        */
#if defined(CONFIG_USE_IRQ)
	sub	r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
#endif
	sub	sp, r0, #12		/* leave 3 words for abort-stack    */

#endif
/*
再次设置栈
(1)第三次设置栈。这次设置栈还是在DDR中,之前虽然已经在DDR中设置过一次栈了,但是本次设置栈的目的是将栈放在比较合适(安全,紧凑而不浪费内存)的地方。
(2)我们实际将栈设置在uboot起始地址上方2MB处,这样安全的栈空间是:2MB-uboot大小-0x1000=1.8MB左右。这个空间既没有太浪费内存,又足够安全。*/
clear_bss:
	ldr	r0, _bss_start		/* find start of bss segment        */
	ldr	r1, _bss_end		/* stop here                        */
	mov 	r2, #0x00000000		/* clear                            */
/*清理bss段代码。注意表示bss段的开头和结尾地址的符号是从链接脚本u-boot.lds得来的。*/
clbss_l:
	str	r2, [r0]		/* clear loop...                    */
	add	r0, r0, #4
	cmp	r0, r1
	ble	clbss_l
	
	ldr	pc, _start_armboot

_start_armboot:
	.word start_armboot
/*tart_armboot是uboot/lib_arm/board.c中,这是一个C语言实现的函数。这个函数就是uboot的第二阶段。
ldr	pc, _start_armboot这句代码的作用就是将uboot第二阶段执行的函数的地址传给pc,实际上就是使用一个远跳转直接跳转到DDR中的第二阶段开始地址处。
(2)远跳转的含义就是这句话加载的地址和当前运行地址无关,而和链接地址有关。因此这个远跳转可以实现从SRAM中的第一阶段跳转到DDR中的第二阶段。
(3)这里这个远跳转就是uboot第一阶段和第二阶段的分界线。*/
#if defined(CONFIG_ENABLE_MMU)
_mmu_table_base:
	.word mmu_table
#endif
-----------------------------------------------------下面这些不分析----------------------------------------------------------------------------
/*
 * copy U-Boot to SDRAM and jump to ram (from NAND or OneNAND)
 * r0: size to be compared
 * Load 1'st 2blocks to RAM because U-boot's size is larger than 1block(128k) size
 */
	.globl copy_from_nand
copy_from_nand:
	push	{lr}		/* save return address */

	mov	r9, r0
	
	mov	r9, #0x100		/* Compare about 8KB */
	bl	copy_uboot_to_ram
	tst 	r0, #0x0
	bne	copy_failed

#if defined(CONFIG_EVT1)
	ldr	r0, =0xd0020000
#else	
	ldr	r0, =0xd0030000
#endif
	ldr	r1, _TEXT_PHY_BASE	/* 0x23e00000 */

#if !defined(CONFIG_SECURE_BOOT)
1:	ldr	r3, [r0], #4
	ldr	r4, [r1], #4
	teq	r3, r4
	bne	compare_failed	/* not matched */
	subs	r9, r9, #4
	bne	1b
#endif
	pop	{pc}		/* all is OK */

copy_failed:
	nop			/* copy from nand failed */
	b	copy_failed

compare_failed:
	nop			/* compare failed */
	b	compare_failed

/*
 * we assume that cache operation is done before. (eg. cleanup_before_linux())
 * actually, we don't need to do anything about cache if not use d-cache in U-Boot
 * So, in this function we clean only MMU. by scsuh
 *
 * void	theLastJump(void *kernel, int arch_num, uint boot_params);
 */
#if defined(CONFIG_ENABLE_MMU)
	.globl theLastJump
theLastJump:
	mov	r9, r0
	ldr	r3, =0xfff00000
	ldr	r4, _TEXT_PHY_BASE
	adr	r5, phy_last_jump
	bic	r5, r5, r3
	orr	r5, r5, r4
	mov	pc, r5
phy_last_jump:
	/*
	 * disable MMU stuff
	 */
	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

	mcr	p15, 0, r0, c8, c7, 0	/* flush v4 TLB */

	mov	r0, #0
	mov	pc, r9
#endif
/*
 *************************************************************************
 *
 * Interrupt handling
 *
 *************************************************************************
 */
@
@ IRQ stack frame.
@
#define S_FRAME_SIZE	72

#define S_OLD_R0	68
#define S_PSR		64
#define S_PC		60
#define S_LR		56
#define S_SP		52

#define S_IP		48
#define S_FP		44
#define S_R10		40
#define S_R9		36
#define S_R8		32
#define S_R7		28
#define S_R6		24
#define S_R5		20
#define S_R4		16
#define S_R3		12
#define S_R2		8
#define S_R1		4
#define S_R0		0

#define MODE_SVC 0x13
#define I_BIT	 0x80

/*
 * use bad_save_user_regs for abort/prefetch/undef/swi ...
 * use irq_save_user_regs / irq_restore_user_regs for IRQ/FIQ handling
 */

	.macro	bad_save_user_regs
	sub	sp, sp, #S_FRAME_SIZE		@ carve out a frame on current user stack
	stmia	sp, {r0 - r12}			@ Save user registers (now in svc mode) r0-r12

	ldr	r2, _armboot_start
	sub	r2, r2, #(CFG_MALLOC_LEN)
	sub	r2, r2, #(CFG_GBL_DATA_SIZE+8)	@ set base 2 words into abort stack
	ldmia	r2, {r2 - r3}			@ get values for "aborted" pc and cpsr (into parm regs)
	add	r0, sp, #S_FRAME_SIZE		@ grab pointer to old stack

	add	r5, sp, #S_SP
	mov	r1, lr
	stmia	r5, {r0 - r3}			@ save sp_SVC, lr_SVC, pc, cpsr
	mov	r0, sp				@ save current stack into r0 (param register)
	.endm

	.macro	irq_save_user_regs
	sub	sp, sp, #S_FRAME_SIZE
	stmia	sp, {r0 - r12}			@ Calling r0-r12
	add	r8, sp, #S_PC			@ !!!! R8 NEEDS to be saved !!!! a reserved stack spot would be good.
	stmdb	r8, {sp, lr}^			@ Calling SP, LR
	str	lr, [r8, #0]			@ Save calling PC
	mrs	r6, spsr
	str	r6, [r8, #4]			@ Save CPSR
	str	r0, [r8, #8]			@ Save OLD_R0
	mov	r0, sp
	.endm

	.macro	irq_restore_user_regs
	ldmia	sp, {r0 - lr}^			@ Calling r0 - lr
	mov	r0, r0
	ldr	lr, [sp, #S_PC]			@ Get PC
	add	sp, sp, #S_FRAME_SIZE
	subs	pc, lr, #4			@ return & move spsr_svc into cpsr
	.endm

	.macro get_bad_stack
	ldr	r13, _armboot_start		@ setup our mode stack (enter in banked mode)
	sub	r13, r13, #(CFG_MALLOC_LEN)	@ move past malloc pool
	sub	r13, r13, #(CFG_GBL_DATA_SIZE+8) @ move to reserved a couple spots for abort stack

	str	lr, [r13]			@ save caller lr in position 0 of saved stack
	mrs	lr, spsr			@ get the spsr
	str	lr, [r13, #4]			@ save spsr in position 1 of saved stack

	mov	r13, #MODE_SVC			@ prepare SVC-Mode
	@ msr	spsr_c, r13
	msr	spsr, r13			@ switch modes, make sure moves will execute
	mov	lr, pc				@ capture return pc
	movs	pc, lr				@ jump to next instruction & switch modes.
	.endm

	.macro get_bad_stack_swi
	sub	r13, r13, #4			@ space on current stack for scratch reg.
	str	r0, [r13]			@ save R0's value.
	ldr	r0, _armboot_start		@ get data regions start
	sub	r0, r0, #(CFG_MALLOC_LEN)	@ move past malloc pool
	sub	r0, r0, #(CFG_GBL_DATA_SIZE+8)	@ move past gbl and a couple spots for abort stack
	str	lr, [r0]			@ save caller lr in position 0 of saved stack
	mrs	r0, spsr			@ get the spsr
	str	lr, [r0, #4]			@ save spsr in position 1 of saved stack
	ldr	r0, [r13]			@ restore r0
	add	r13, r13, #4			@ pop stack entry
	.endm

	.macro get_irq_stack			@ setup IRQ stack
	ldr	sp, IRQ_STACK_START
	.endm

	.macro get_fiq_stack			@ setup FIQ stack
	ldr	sp, FIQ_STACK_START
	.endm

/*
 * exception handlers
 */
	.align	5
undefined_instruction:
	get_bad_stack
	bad_save_user_regs
	bl	do_undefined_instruction

	.align	5
software_interrupt:
	get_bad_stack_swi
	bad_save_user_regs
	bl	do_software_interrupt

	.align	5
prefetch_abort:
	get_bad_stack
	bad_save_user_regs
	bl	do_prefetch_abort

	.align	5
data_abort:
	get_bad_stack
	bad_save_user_regs
	bl	do_data_abort

	.align	5
not_used:
	get_bad_stack
	bad_save_user_regs
	bl	do_not_used

#if defined(CONFIG_USE_IRQ)

	.align	5
irq:
	get_irq_stack
	irq_save_user_regs
	bl	do_irq
	irq_restore_user_regs

	.align	5
fiq:
	get_fiq_stack
	/* someone ought to write a more effiction fiq_save_user_regs */
	irq_save_user_regs
	bl	do_fiq
	irq_restore_user_regs

#else

	.align	5
irq:
	get_bad_stack
	bad_save_user_regs
	bl	do_irq

	.align	5
fiq:
	get_bad_stack
	bad_save_user_regs
	bl	do_fiq

#endif
	.align 5
.global arm_cache_flush
arm_cache_flush:
       mcr     p15, 0, r1, c7, c5, 0           @ invalidate I cache
       mov     pc, lr                          @ back to caller

/*
 *     v7_flush_dcache_all()
 *
 *     Flush the whole D-cache.
 *
 *     Corrupted registers: r0-r5, r7, r9-r11
 *
 *     - mm    - mm_struct describing address space
 */
       .align 5
.global v7_flush_dcache_all
v7_flush_dcache_all:

	ldr	r0, =0xffffffff
	mrc	p15, 1, r0, c0, c0, 1 		@ Read CLIDR
	ands	r3, r0, #0x7000000
	mov	r3, r3, LSR #23       		@ Cache level value (naturally aligned)
	beq 	Finished
	mov	r10, #0
Loop1:         
	add	r2, r10, r10, LSR #1  		@ Work out 3xcachelevel
	mov	r1, r0, LSR r2        		@ bottom 3 bits are the Ctype for this level
	and	r1, r1, #7            		@ get those 3 bits alone
	cmp	r1, #2
	blt	Skip                   		@ no cache or only instruction cache at this level
	mcr	p15, 2, r10, c0, c0, 0 		@ write the Cache Size selection register
	mov	r1, #0
	mcr	p15, 0, r1, c7, c5, 4 		@ PrefetchFlush to sync the change to the CacheSizeID reg
	mrc	p15, 1, r1, c0, c0, 0 		@ reads current Cache Size ID register
	and	r2, r1, #0x7           		@ extract the line length field
	add	r2, r2, #4            		@ add 4 for the line length offset (log2 16 bytes)
	ldr	r4, =0x3FF
	ands	r4, r4, r1, LSR #3   		@ R4 is the max number on the way size (right aligned)
	clz	r5, r4                		@ R5 is the bit position of the way size increment
	ldr	r7, =0x00007FFF
	ands	r7, r7, r1, LSR #13  		@ R7 is the max number of the index size (right aligned)
Loop2:         
	mov	r9, r4                      	@ R9 working copy of the max way size (right aligned)
Loop3:         
	orr	r11, r10, r9, LSL r5        	@ factor in the way number and cache number into R11
	orr	r11, r11, r7, LSL r2        	@ factor in the index number
	mcr	p15, 0, r11, c7, c6, 2 		@ invalidate by set/way
	subs	r9, r9, #1                 	@ decrement the way number
	bge	Loop3
	subs	r7, r7, #1                 	@ decrement the index
	bge	Loop2
Skip:          
	add	r10, r10, #2                	@ increment the cache number
	cmp	r3, r10
	bgt	Loop1
Finished:
	mov	pc, lr
	
       .align  5
.global disable_l2cache
disable_l2cache:
	mrc     p15, 0, r0, c1, c0, 1
	bic     r0, r0, #(1<<1)
	mcr     p15, 0, r0, c1, c0, 1
	mov	pc, lr


       .align  5
.global enable_l2cache
enable_l2cache:
	mrc     p15, 0, r0, c1, c0, 1
	orr     r0, r0, #(1<<1)
	mcr     p15, 0, r0, c1, c0, 1
	mov     pc, lr

       .align  5
.global set_l2cache_auxctrl
set_l2cache_auxctrl:
	mov	r0, #0x0
	mcr     p15, 1, r0, c9, c0, 2
	mov     pc, lr

       .align  5
.global set_l2cache_auxctrl_cycle
set_l2cache_auxctrl_cycle:
	mrc 	p15, 1, r0, c9, c0, 2
	bic 	r0, r0, #(0x1<<29)
	bic 	r0, r0, #(0x1<<21)
	bic 	r0, r0, #(0x7<<6)
	bic 	r0, r0, #(0x7<<0)
	mcr 	p15, 1, r0, c9, c0, 2
	mov     pc,lr

	.align 5
CoInvalidateDCacheIndex:
	;/* r0 = index */
	mcr     p15, 0, r0, c7, c6, 2
	mov     pc,lr


#if defined(CONFIG_INTEGRATOR) && defined(CONFIG_ARCH_CINTEGRATOR)
/* Use the IntegratorCP function from board/integratorcp/platform.S */
#elif defined(CONFIG_S5PC11X)
/* For future usage of S3C64XX*/
#else
	.align	5
.globl reset_cpu
reset_cpu:
	ldr	r1, rstctl	/* get addr for global reset reg */
	mov	r3, #0x2	/* full reset pll+mpu */
	str	r3, [r1]	/* force reset */
	mov	r0, r0
_loop_forever:
	b	_loop_forever
rstctl:
	.word	PM_RSTCTRL_WKUP

#endif

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值