#define DECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm ("r8")
这个宏用来定义gd_t类型的gd指针,后面的asm ("r8")代表指定arm里的r8寄存器存储。gd_t里面存储着很多全局变量供整个uboot使用,里面还有个bd_t存放了开发板的硬件相关的参数,这里用宏定义来声明这个变量,需要用到gd_t的地方就使用宏DECLARE_GLOBAL_DATA_PTR。
这个是在asm-arm/gloal_data.h文件里定义的全局变量gd_t,里面还用到了一个在asm-arm/uboot.h里定义的bd_t。
typedefstruct 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;
phys_size_t ram_size; /* RAM size */
unsigned long reset_status; /* reset status register at boot */
#endif
void **jt; /* jump table */
} gd_t;
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];//CONFIG_NR_DRAM_BANKS
2
#ifdef CONFIG_HAS_ETH1
/* second onboard ethernet port */
unsigned char bi_enet1addr[6];
#endif
} bd_t;
init_fnc_t *init_sequence[] = {
cpu_init, /* basic cpu dependent setup */
int cpu_init (void)
{
/*
* setup up stacks if necessary
*/
#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;
}
|
cpu_init没有做任何事,因为在汇编的时候已经将cpu相关的部件都初始化了。
|
#if defined(CONFIG_SKIP_RELOCATE_UBOOT)//宏未定义
reloc_init, /* Set the relocation done flag, must
do this AFTER cpu_init(), but as soon
as possible */
#endif
board_init, /* basic board dependent setup */
int board_init(void)
{
DECLARE_GLOBAL_DATA_PTR;
#ifdef CONFIG_DRIVER_SMC911X//宏未定义
smc9115_pre_init();
#endif
#ifdef CONFIG_DRIVER_DM9000
dm9000_pre_init();
#endif
gd->bd->bi_arch_number = MACH_TYPE;
gd->bd->bi_boot_params = (PHYS_SDRAM_1+0x100);
return 0;
}
|
这里board_init函数主要做了两件事,一件是运行dm9000_pre_init()函数,另一个是给bi_arch_number和
bi_boot_params变量赋值,bi就是board_info,两个变量都是开发板的参数。
arch_number是机器码,就是uboot给这个开发板定义的一个唯一编号,主要作用就是在uboot和linux内核之间进
行比对和适配。因为嵌入式设备都是高度定制化的,不同的开发板使用的硬件不同,移植的内核镜像也不同,不
能随意更改。
boot_params则是记录板子的SDRAM的物理地址,uboot将来需要得到硬件配置时就读取bi_相关的变量即可。
|
static void dm9000_pre_init(void)
{
unsigned int tmp;
#if defined(DM9000_16BIT_DATA)//宏定义了
SROM_BW_REG &= ~(0xf << 4);//SROM_BW_REG=0xE800_0000
SROM_BW_REG |= (1<<7) | (1<<6) | (1<<5) | (1<<4);
#else
SROM_BW_REG &= ~(0xf << 4);
SROM_BW_REG |= (0<<6) | (0<<5) | (0<<4);
#endif
SROM_BC1_REG = ((0<<28)|(1<<24)|(5<<16)|(1<<12)|(4<<8)|(6<<4)|(0<<0));//uboot
//SROM_BC1_REG = ((0<<28)|(0<<24)|(5<<16)|(0<<12)|(0<<8)|(0<<4)|(0<<0));//kernel
tmp = MP01CON_REG;//0xE0200000
+ 0x2E0
tmp &=~(0xf<<4);
tmp |=(2<<4);
MP01CON_REG = tmp;
}
|
不是很清楚具体做什么,查数据手册SROM_BW_REG是设置SROM controller的,相当于配置SROM,
SROM_BC1_REG是配置T周期的,MP01CON_REG是使能GPIO为SROM_CSn[0]。查dm9000是网卡型号,根据函数
名来看这里应该是进行网卡相关配置的一个预处理。
2017/11/10 增补:
SROM是( SRAM或ROM )的统称,SoC的SROMController其实就是SoC提供的对外总线式连接SRAM/ROM的接
口。如果SoC要外部外接一些SRAM/ROM类的存储芯片(或者伪装成SROM接口的芯片,譬如网卡芯片)就要通
过SROM Controller来连接。网卡接在SROM中好处就是网卡芯片好像一个存储芯片一样被扩展在SoC的一个地址
空间中,主机SoC可以直接用一个地址来访问网卡芯片内部寄存器。
查原理图可知 DM9000 接了 SoC 的 CSn1,即接在了 bank1 上,因此使用 SROMcontroller1 来控制,这里就是
相对应的初始化操作。
|
interrupt_init, /* set up exceptions */
int interrupt_init(void)
{
S5PC11X_TIMERS *const timers = S5PC11X_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) {
/*
* for 10 ms clock period @ PCLK with 4 bit divider = 1/2
* (default) and prescaler = 16. Should be 10390
* @33.25MHz and @ 66 MHz
*/
timer_load_val = get_PCLK() / (16 * 100);
}
/* load value for 10 ms timeout */
lastdec = timers->TCNTB4 = timer_load_val;
/* auto load, manual update of Timer 4 */
timers->TCON = (timers->TCON & ~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;
return (0);
}
|
这里是用来配置时钟的。get_PCLK()函数是用来得到PCLK_PSYS的的时钟频率,get_PCLK又是根
据get_HCLK得到的HCLK的时钟频率,如此,最终只在get_PLLCLK函数中是通过访问寄存器来得
到时钟频率,其他的函数都是通过函数来得到对应CLK的时钟频率。
另外这里使用的是Timer4定时器,这个定时器没有引脚,无法输出pwm波形,所以这个计时只能采
用轮询方式而不能使用中断,典型的应用就是bootdelay,在bootdelay的时候只进行计时和检测按
键,无法做其他操作。
|
env_init, /* initialize environment */
环境变量的初始化,这个函数只是对内存里维护的那一份uboot的env做了基本的初始化或者说是判定(判定里面有没有能用的环境变量)。当前因为我们还没进行环境变量从SD卡到DDR中的relocate,因此当前环境变量是不能用的。在start_armboot函数中调用env_relocate才进行环境变量从SD卡中到DDR中的重定位。重定位之后需要环境变量时才可以从DDR中去取,重定位之前如果要使用环境变量只能从SD卡中去读取。
init_baudrate, /* initialze baudrate settings */
static int init_baudrate (void)
{
char tmp[64]; /* long enough for environment variables */
int i = getenv_r ("baudrate", tmp, sizeof (tmp));
gd->bd->bi_baudrate = gd->baudrate = (i > 0)
? (int) simple_strtoul (tmp, NULL, 10)
: CONFIG_BAUDRATE;
return (0);
}
|
初始化串口通信的波特率,先去环境变量中读取"baudrate"这个环境变量的值。如果读取成功则使
用这个值作为环境变量,记录在gd->baudrate和gd->bd->bi_baudrate中;如果读取不成功则使用
x210_sd.h中的的CONFIG_BAUDRATE的值作为波特率。
|
serial_init, /* serial communications setup */
初始化串口,实际上在汇编阶段已经被初始化过了,跳转到定义发现函数和cpu_init类似,什么都没做。
console_init_f, /* stage 1 init of console */
控制台第一阶段初始化,后面的_f表示第一阶段,_r表示第二阶段。这里只是将gd->have_console = 1;其他的都没做。
display_banner, /* say that we are here */
显示logo,整个函数实际上只有两句printf ("\n\n%s\n\n", version_string);这一句用来打印uboot版本信息。open_backlight();//lqm.打开背光。实际的display_banner函数中还有几条用来调试的debug()宏。
version_string:
const char version_string[] =
U_BOOT_VERSION" (" __DATE__ " - " __TIME__ ")"CONFIG_IDENT_STRING;
实际是打印了U_BOOT_VERSION宏,而这个宏是没有在源码中定义的,是通过Makefile传递给uboot的。
#if defined(CONFIG_DISPLAY_CPUINFO)
print_cpuinfo, /* display cpu info (and speed) */
int print_cpuinfo(void)
{
uint set_speed;
uint tmp;
uchar result_set;
#if defined(CONFIG_CLK_533_133_100_100)
set_speed = 53300;
#elif defined(CONFIG_CLK_667_166_166_133)
set_speed = 66700;
#elif defined(CONFIG_CLK_800_200_166_133)
set_speed = 80000;
#elif defined(CONFIG_CLK_1000_200_166_133)
set_speed = 100000;
#elif defined(CONFIG_CLK_1200_200_166_133)
set_speed = 120000;
#else
set_speed = 100000;
printf("Any CONFIG_CLK_XXX is not enabled\n");
#endif
tmp = (set_speed / (get_ARMCLK()/1000000));
if((tmp < 105) && (tmp > 95)){
result_set = 1;
} else {
result_set = 0;
}
#ifdef CONFIG_MCP_SINGLE
printf("\nCPU: S5PV210@%ldMHz(%s)\n", get_ARMCLK()/1000000, ((result_set == 1) ? "OK" : "FAIL"));
#else
printf("\nCPU: S5PC110@%ldMHz(%s)\n", get_ARMCLK()/1000000, ((result_set == 1) ? "OK" : "FAIL"));
#endif
printf(" APLL = %ldMHz, HclkMsys = %ldMHz, PclkMsys = %ldMHz\n",
get_FCLK()/1000000, get_HCLK()/1000000, get_PCLK()/1000000);
#if 1
printf("
MPLL = %ldMHz, EPLL = %ldMHz\n",
get_MPLL_CLK()/1000000, get_PLLCLK(EPLL)/1000000);
printf("
HclkDsys = %ldMHz, PclkDsys = %ldMHz\n",
get_HCLKD()/1000000, get_PCLKD()/1000000);
printf("
HclkPsys = %ldMHz, PclkPsys = %ldMHz\n",
get_HCLKP()/1000000, get_PCLKP()/1000000);
printf("
SCLKA2M = %ldMHz\n", get_SCLKA2M()/1000000);
#endif
puts("Serial = CLKUART ");
return 0;
}
|
这个函数用来打印cpu信息,主要是时钟频率,打印出的效果:
|
CPU: S5PV210@1000MHz(OK)
APLL = 1000MHz, HclkMsys = 200MHz, PclkMsys = 100MHz
MPLL = 667MHz, EPLL = 96MHz
HclkDsys = 166MHz, PclkDsys = 83MHz
HclkPsys = 133MHz, PclkPsys = 66MHz
SCLKA2M = 200MHz
Serial = CLKUART
|
#endif
#if defined(CONFIG_DISPLAY_BOARDINFO)
checkboard, /* display board info */
int checkboard(void)
{
#ifdef CONFIG_MCP_SINGLE
#if defined(CONFIG_VOGUES)
printf("\nBoard: VOGUESV210\n");
#else
printf("\nBoard: X210\n");
#endif //CONFIG_VOGUES
#else
printf("\nBoard: X210\n");
#endif
return (0);
}
|
这个函数就是用来检查板子的信息,根据定义的宏打印。
|
#endif
#if defined(CONFIG_HARD_I2C) || defined(CONFIG_SOFT_I2C)
init_func_i2c,
没有使用i2c,因为对应的宏没有定义,在SI中看到CONFIG_HARD_I2C是红色的,跳转过去后发现实际上是没有被定义的。
#undef CONFIG_S3C64XX_I2C /* this board has H/W I2C */
#ifdef CONFIG_S3C64XX_I2C
#define CONFIG_HARD_I2C 1
|
#endif
dram_init, /* configure available RAM banks */
int dram_init(void)
{
DECLARE_GLOBAL_DATA_PTR;
gd->bd->bi_dram[0].start = PHYS_SDRAM_1;
gd->bd->bi_dram[0].size = PHYS_SDRAM_1_SIZE;
#if defined(PHYS_SDRAM_2)
gd->bd->bi_dram[1].start = PHYS_SDRAM_2;
gd->bd->bi_dram[1].size = PHYS_SDRAM_2_SIZE;
#endif
#if defined(PHYS_SDRAM_3)
gd->bd->bi_dram[2].start = PHYS_SDRAM_3;
gd->bd->bi_dram[2].size = PHYS_SDRAM_3_SIZE;
#endif
return 0;
}
|
初始化bi_dram数组中的相关变量。
|
display_dram_config,
显示DRAM的配置。
NULL,
最后一个元素以NULL结尾,这样在循环中就可以作为条件判断来结束循环。
};
init_sequence函数就是把进行初始化的函数都集中在一起,这里面进行的初始化操作有:
-
cpu初始化 (实际为空)
-
板级初始化 网卡预配置,记录机器码和SDRAM信息
-
时钟初始化
-
环境变量初始化 这里还没有将环境变量从SD卡迁移到内存,基本都被判定为无效
-
串口波特率初始化
-
串口初始化 (实际为空)
-
控制台初始化第一阶段
-
打印banner uboot版本信息和日期时间
-
打印cpu信息 各个时钟域的时钟频率
-
检查板子信息并打印
-
初始化i2c (无效,未配置宏)
-
初始化DRAM 配置有几块DRAM和DRAM大小
-
打印DRAM配置信息