上一节已经分析到了uboot的board_init_r函数,并且把两个参数传递给它
/* call board_init_r(gd_t *id, ulong dest_addr) */
/* gd的 地址和 当前新的uboot的起始地址传参给board_init_r */
mov r0, r9 /* gd_t */
ldr r1, [r9, #GD_RELOCADDR] /* dest_addr */
/* call board_init_r */
ldr pc, =board_init_r
接下来就分析uboot的后半部分,也就是
void board_init_r(gd_t *new_gd, ulong dest_addr)
{
#ifdef CONFIG_NEEDS_MANUAL_RELOC //没定义
int i;
#endif
#ifdef CONFIG_AVR32 //没定义
mmu_init_r(dest_addr);
#endif
#if !defined(CONFIG_X86) && !defined(CONFIG_ARM) && !defined(CONFIG_ARM64)
gd = new_gd; //定义了这个CONFIG_ARM,所以不执行
#endif
#ifdef CONFIG_NEEDS_MANUAL_RELOC //没定义
for (i = 0; i < ARRAY_SIZE(init_sequence_r); i++)
init_sequence_r[i] += gd->reloc_off;
#endif
/* 运行函数initcall_run_list,调用init_sequence_r里的函数指针执行 */
if (initcall_run_list(init_sequence_r))
hang();
/* NOTREACHED - run_main_loop() does not return */
hang();
}
initcall_run_list函数我们前面已经分析过了
init_fnc_t init_sequence_r[] = {
initr_trace,
initr_reloc,
/* TODO: could x86/PPC have this also perhaps? */
#ifdef CONFIG_ARM
initr_caches,
/* Note: For Freescale LS2 SoCs, new MMU table is created in DDR.
* A temporary mapping of IFC high region is since removed,
* so environmental variables in NOR flash is not availble
* until board_init() is called below to remap IFC to high
* region.
*/
#endif
initr_reloc_global_data,
#if defined(CONFIG_SYS_INIT_RAM_LOCK) && defined(CONFIG_E500)
initr_unlock_ram_in_cache,
#endif
initr_barrier,
initr_malloc,
initr_console_record,
#ifdef CONFIG_SYS_NONCACHED_MEMORY
initr_noncached,
#endif
bootstage_relocate,
#ifdef CONFIG_DM
initr_dm,
#endif
initr_bootstage,
#if defined(CONFIG_ARM) || defined(CONFIG_NDS32)
board_init, /* Setup chipselects */
#endif
/*
* TODO: printing of the clock inforamtion of the board is now
* implemented as part of bdinfo command. Currently only support for
* davinci SOC's is added. Remove this check once all the board
* implement this.
*/
#ifdef CONFIG_CLOCKS
set_cpu_clk_info, /* Setup clock information */
#endif
#ifdef CONFIG_EFI_LOADER
efi_memory_init,
#endif
stdio_init_tables,
initr_serial,
initr_announce,
INIT_FUNC_WATCHDOG_RESET
#ifdef CONFIG_NEEDS_MANUAL_RELOC
initr_manual_reloc_cmdtable,
#endif
#if defined(CONFIG_PPC) || defined(CONFIG_M68K) || defined(CONFIG_MIPS)
initr_trap,
#endif
#ifdef CONFIG_ADDR_MAP
initr_addr_map,
#endif
#if defined(CONFIG_BOARD_EARLY_INIT_R)
board_early_init_r,
#endif
INIT_FUNC_WATCHDOG_RESET
#ifdef CONFIG_LOGBUFFER
initr_logbuffer,
#endif
#ifdef CONFIG_POST
initr_post_backlog,
#endif
INIT_FUNC_WATCHDOG_RESET
#ifdef CONFIG_SYS_DELAYED_ICACHE
initr_icache_enable,
#endif
#if defined(CONFIG_PCI) && defined(CONFIG_SYS_EARLY_PCI_INIT)
/*
* Do early PCI configuration _before_ the flash gets initialised,
* because PCU ressources are crucial for flash access on some boards.
*/
initr_pci,
#endif
#ifdef CONFIG_WINBOND_83C553
initr_w83c553f,
#endif
#ifdef CONFIG_ARCH_EARLY_INIT_R
arch_early_init_r,
#endif
power_init_board,
#ifndef CONFIG_SYS_NO_FLASH
initr_flash,
#endif
INIT_FUNC_WATCHDOG_RESET
#if defined(CONFIG_PPC) || defined(CONFIG_M68K) || defined(CONFIG_X86) || \
defined(CONFIG_SPARC)
/* initialize higher level parts of CPU like time base and timers */
cpu_init_r,
#endif
#ifdef CONFIG_PPC
initr_spi,
#endif
#ifdef CONFIG_CMD_NAND
initr_nand,
#endif
#ifdef CONFIG_CMD_ONENAND
initr_onenand,
#endif
#ifdef CONFIG_GENERIC_MMC
initr_mmc,
#endif
#ifdef CONFIG_HAS_DATAFLASH
initr_dataflash,
#endif
initr_env,
#ifdef CONFIG_SYS_BOOTPARAMS_LEN
initr_malloc_bootparams,
#endif
INIT_FUNC_WATCHDOG_RESET
initr_secondary_cpu,
#if defined(CONFIG_ID_EEPROM) || defined(CONFIG_SYS_I2C_MAC_OFFSET)
mac_read_from_eeprom,
#endif
INIT_FUNC_WATCHDOG_RESET
#if defined(CONFIG_PCI) && !defined(CONFIG_SYS_EARLY_PCI_INIT)
/*
* Do pci configuration
*/
initr_pci,
#endif
stdio_add_devices,
initr_jumptable,
#ifdef CONFIG_API
initr_api,
#endif
console_init_r, /* fully init console as a device */
#ifdef CONFIG_DISPLAY_BOARDINFO_LATE
show_board_info,
#endif
#ifdef CONFIG_ARCH_MISC_INIT
arch_misc_init, /* miscellaneous arch-dependent init */
#endif
#ifdef CONFIG_MISC_INIT_R
misc_init_r, /* miscellaneous platform-dependent init */
#endif
INIT_FUNC_WATCHDOG_RESET
#ifdef CONFIG_CMD_KGDB
initr_kgdb,
#endif
interrupt_init,
#if defined(CONFIG_ARM) || defined(CONFIG_AVR32)
initr_enable_interrupts,
#endif
#if defined(CONFIG_MICROBLAZE) || defined(CONFIG_AVR32) || defined(CONFIG_M68K)
timer_init, /* initialize timer */
#endif
#if defined(CONFIG_STATUS_LED)
initr_status_led,
#endif
/* PPC has a udelay(20) here dating from 2002. Why? */
#ifdef CONFIG_CMD_NET
initr_ethaddr,
#endif
#ifdef CONFIG_BOARD_LATE_INIT
board_late_init,
#endif
#if defined(CONFIG_CMD_AMBAPP)
ambapp_init_reloc,
#if defined(CONFIG_SYS_AMBAPP_PRINT_ON_STARTUP)
initr_ambapp_print,
#endif
#endif
#if defined(CONFIG_SCSI) && !defined(CONFIG_DM_SCSI)
INIT_FUNC_WATCHDOG_RESET
initr_scsi,
#endif
#ifdef CONFIG_CMD_DOC
INIT_FUNC_WATCHDOG_RESET
initr_doc,
#endif
#ifdef CONFIG_BITBANGMII
initr_bbmii,
#endif
#ifdef CONFIG_CMD_NET
INIT_FUNC_WATCHDOG_RESET
initr_net,
#endif
#ifdef CONFIG_POST
initr_post,
#endif
#if defined(CONFIG_CMD_PCMCIA) && !defined(CONFIG_CMD_IDE)
initr_pcmcia,
#endif
#if defined(CONFIG_CMD_IDE)
initr_ide,
#endif
#ifdef CONFIG_LAST_STAGE_INIT
INIT_FUNC_WATCHDOG_RESET
/*
* Some parts can be only initialized if all others (like
* Interrupts) are up and running (i.e. the PC-style ISA
* keyboard).
*/
last_stage_init,
#endif
#ifdef CONFIG_CMD_BEDBUG
INIT_FUNC_WATCHDOG_RESET
initr_bedbug,
#endif
#if defined(CONFIG_PRAM) || defined(CONFIG_LOGBUFFER)
initr_mem,
#endif
#ifdef CONFIG_PS2KBD
initr_kbd,
#endif
#if defined(CONFIG_SPARC)
prom_init,
#endif
run_main_loop,
};
1.1、initr_trace
static int initr_trace(void)
{
#ifdef CONFIG_TRACE //没定义
trace_init(gd->trace_buff, CONFIG_TRACE_BUFFER_SIZE);
#endif
return 0;
}
1.2、initr_reloc
static int initr_reloc(void)
{
/* tell others: relocation done */
gd->flags |= GD_FLG_RELOC | GD_FLG_FULL_MALLOC_INIT; /* 标记已经重定位成功,malloc初始化 */
return 0;
}
1.3、initr_caches
static int initr_caches(void)
{
/* Enable caches */
enable_caches(); //使能cache
return 0;
}
1.4、下面我们就初始化了全局变量monitor_flash_len 的长度
static int initr_reloc_global_data(void)
{
#ifdef __ARM__ /* 定义了 */
monitor_flash_len = _end - __image_copy_start;
#elif defined(CONFIG_NDS32)
monitor_flash_len = (ulong)&_end - (ulong)&_start;
#elif !defined(CONFIG_SANDBOX) && !defined(CONFIG_NIOS2)
monitor_flash_len = (ulong)&__init_end - gd->relocaddr;
#endif
#if defined(CONFIG_MPC85xx) || defined(CONFIG_MPC86xx) /* 没定义 */
/*
* The gd->cpu pointer is set to an address in flash before relocation.
* We need to update it to point to the same CPU entry in RAM.
* TODO: why not just add gd->reloc_ofs?
*/
gd->arch.cpu += gd->relocaddr - CONFIG_SYS_MONITOR_BASE;
/*
* If we didn't know the cpu mask & # cores, we can save them of
* now rather than 'computing' them constantly
*/
fixup_cpu();
#endif
#ifdef CONFIG_SYS_EXTRA_ENV_RELOC /* 没定义 */
/*
* Some systems need to relocate the env_addr pointer early because the
* location it points to will get invalidated before env_relocate is
* called. One example is on systems that might use a L2 or L3 cache
* in SRAM mode and initialize that cache from SRAM mode back to being
* a cache in cpu_init_r.
*/
gd->env_addr += gd->relocaddr - CONFIG_SYS_MONITOR_BASE;
#endif
#ifdef CONFIG_OF_EMBED /* 没定义 */
/*
* The fdt_blob needs to be moved to new relocation address
* incase of FDT blob is embedded with in image
*/
gd->fdt_blob += gd->reloc_off;
#endif
#ifdef CONFIG_EFI_LOADER /* 没定义 */
efi_runtime_relocate(gd->relocaddr, NULL);
#endif
return 0;
}
1.5、initr_malloc
static int initr_malloc(void)
{
ulong malloc_start;
#ifdef CONFIG_SYS_MALLOC_F_LEN
debug("Pre-reloc malloc() used %#lx bytes (%ld KB)\n", gd->malloc_ptr,
gd->malloc_ptr / 1024);
#endif
/* The malloc area is immediately below the monitor copy in DRAM */
malloc_start = gd->relocaddr - TOTAL_MALLOC_LEN; /* 我们定义了80M+4k的malloc */
mem_malloc_init((ulong)map_sysmem(malloc_start, TOTAL_MALLOC_LEN),
TOTAL_MALLOC_LEN);
return 0;
}
malloc长度定义
#define CONFIG_ENV_SIZE 4096
/* Size of malloc() pool before and after relocation */
#define CONFIG_SYS_MALLOC_LEN (CONFIG_ENV_SIZE + (80 << 20))
#define TOTAL_MALLOC_LEN CONFIG_SYS_MALLOC_LEN
初始化
void mem_malloc_init(ulong start, ulong size)
{
mem_malloc_start = start;
mem_malloc_end = start + size;
mem_malloc_brk = start;
debug("using memory %#lx-%#lx for malloc()\n", mem_malloc_start,
mem_malloc_end);
#ifdef CONFIG_SYS_MALLOC_CLEAR_ON_INIT /* 定义这个宏则堆区初始化为0,否则不初始化 */
memset((void *)mem_malloc_start, 0x0, size);
#endif
malloc_bin_reloc();
}
1.6、bootstage_relocate
int bootstage_relocate(void)
{
int i;
/*
* Duplicate all strings. They may point to an old location in the
* program .text section that can eventually get trashed.
*/
for (i = 0; i < BOOTSTAGE_ID_COUNT; i++)
if (record[i].name)
record[i].name = strdup(record[i].name); /* 重定位bootargs的参数,以 */
return 0;
}
char * strdup(const char *s)
{
char *new;
if ((s == NULL) || /* 采用动态内存存储,将来以哈希表的方式搜索 */
((new = malloc (strlen(s) + 1)) == NULL) ) {
return NULL;
}
strcpy (new, s);
return new;
}
1.7、initr_dm
static int initr_dm(void)
{
int ret;
/* Save the pre-reloc driver model and start a new one */
gd->dm_root_f = gd->dm_root;
gd->dm_root = NULL;
#ifdef CONFIG_TIMER
gd->timer = NULL;
#endif
/* 设备树相关的,先跳过 */
ret = dm_init_and_scan(false);
if (ret)
return ret;
#ifdef CONFIG_TIMER_EARLY
ret = dm_timer_init();
if (ret)
return ret;
#endif
return 0;
}
1.8、initr_bootstage 把动态内存分配的bootargs处理一下
static int initr_bootstage(void)
{
/* We cannot do this before initr_dm() */
bootstage_mark_name(BOOTSTAGE_ID_START_UBOOT_R, "board_init_r");
return 0;
}
1.9、board_init
int board_init(void)
{
/* Set Initial global variables */
gd->bd->bi_arch_number = MACH_TYPE_GONI; /* 这里我们修改成MACH_TYPE_SMDKV210 */
gd->bd->bi_boot_params = PHYS_SDRAM_1 + 0x100; /* 普通传参就是在这个地址 */
return 0;
}
1.10、stdio_init_tables
int stdio_init_tables(void)
{
#if defined(CONFIG_NEEDS_MANUAL_RELOC) /* 这个没定义 */
/* 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 /* CONFIG_NEEDS_MANUAL_RELOC */
/* Initialize the list */
INIT_LIST_HEAD(&(devs.list)); /* 初始化一下设备链表 */
return 0;
}
1.11、initr_serial
static int initr_serial(void)
{
serial_initialize();
return 0;
}
/* Called prior to relocation */
int serial_init(void)
{
serial_find_console_or_panic(); /* 再一次初始化串口, */
gd->flags |= GD_FLG_SERIAL_READY; /* 并标记串口初始化成功 */
return 0;
}
/* Called after relocation */
void serial_initialize(void)
{
serial_init();
}
1.12、initr_onenand goni板子用了NAND(onenand),我们的板子是INAND,所以这个CONFIG_CMD_ONENAND要注释掉
#if defined(CONFIG_CMD_ONENAND)
static int initr_onenand(void)
{
puts("NAND: ");
onenand_init();
return 0;
}
#endif
用 //注释掉下面几个和ONENAND相关的宏后,编译出现告警
//#define CONFIG_CMD_ONENAND
//#define CONFIG_USE_ONENAND_BOARD_INIT
//#define CONFIG_SAMSUNG_ONENAND 1
//#define CONFIG_SYS_ONENAND_BASE 0xB0000000
LDS u-boot.lds
LD u-boot
arm-linux-ld:u-boot.lds:1: ignoring invalid character `#' in expression
arm-linux-ld:u-boot.lds:1: syntax error
Makefile:1018: recipe for target 'u-boot' failed
make: *** [u-boot] Error 1
各种查找原因,发现shan掉这几个宏或用 #if 0 #endif就可以,有点小坑
接下来没有告警,打印出来更多信息了
1.1.13、initr_env
static int initr_env(void)
{
/* initialize environment */
if (should_load_env())
env_relocate();
else
set_default_env(NULL);
#ifdef CONFIG_OF_CONTROL /* 定义了 */
setenv_addr("fdtcontroladdr", gd->fdt_blob);
#endif
/* Initialize from environment,初始化环境变量 */
load_addr = getenv_ulong("loadaddr", 16, load_addr);
#if defined(CONFIG_SYS_EXTBDINFO) /* 没定义 */
#if defined(CONFIG_405GP) || defined(CONFIG_405EP)
#if defined(CONFIG_I2CFAST)
/*
* set bi_iic_fast for linux taking environment variable
* "i2cfast" into account
*/
{
char *s = getenv("i2cfast");
if (s && ((*s == 'y') || (*s == 'Y'))) {
gd->bd->bi_iic_fast[0] = 1;
gd->bd->bi_iic_fast[1] = 1;
}
}
#endif /* CONFIG_I2CFAST */
#endif /* CONFIG_405GP, CONFIG_405EP */
#endif /* CONFIG_SYS_EXTBDINFO */
return 0;
}
/*
* Tell if it's OK to load the environment early in boot.
*
* If CONFIG_OF_CONTROL is defined, we'll check with the FDT to see
* if this is OK (defaulting to saying it's OK).
*
* NOTE: Loading the environment early can be a bad idea if security is
* important, since no verification is done on the environment.
*
* @return 0 if environment should not be loaded, !=0 if it is ok to load
* return 0不加载环境,!= 0如果可以加载
*/
static int should_load_env(void)
{
#ifdef CONFIG_OF_CONTROL /* 定义了,fdt的这个我们先跳过 */
return fdtdec_get_config_int(gd->fdt_blob, "load-environment", 1);
#elif defined CONFIG_DELAY_ENVIRONMENT
return 0;
#else
return 1;
#endif
}
既然定义了,那应该是可以正常加载了,‘根据打印出来的调试信息继续分析
’
void env_relocate(void)
{
#if defined(CONFIG_NEEDS_MANUAL_RELOC) /* 没定义 */
env_reloc();
env_htab.change_ok += gd->reloc_off;
#endif
if (gd->env_valid == 0) {
#if defined(CONFIG_ENV_IS_NOWHERE) || defined(CONFIG_SPL_BUILD) /* 这连个都没定义 */
/* Environment not changable */
set_default_env(NULL);
#else
bootstage_error(BOOTSTAGE_ID_NET_CHECKSUM); /* 我们网络都没定义,所以也肯定测不出个所以然来 */
set_default_env("!bad CRC"); /* 调试信息有打印出这个,所以这函数分析一下 */
#endif
} else {
env_relocate_spec();
}
}
void set_default_env(const char *s)
{
int flags = 0;
/* 我们的环境变量没配置过,正常是不会超出范围 */
if (sizeof(default_environment) > ENV_SIZE) {
puts("*** Error - default environment is too large\n\n");
return;
}
/* 上面传进来的是"!bad CRC" */
if (s) {
if (*s == '!') { /* 第一个字符是'!' */
printf("*** Warning - %s, "
"using default environment\n\n",
s + 1); /* 跳过!,打印我们看到的一样 */
} else {
flags = H_INTERACTIVE;
puts(s);
}
} else {
puts("Using default environment\n\n");
}
if (himport_r(&env_htab, (char *)default_environment,
sizeof(default_environment), '\0', flags, 0,
0, NULL) == 0)
error("Environment import failed: errno = %d\n", errno);
/* 标记使用默认环境变量和环境变量成功 */
gd->flags |= GD_FLG_ENV_READY;
gd->flags |= GD_FLG_ENV_DEFAULT;
}
1.13、initr_mmc
static int initr_mmc(void)
{
puts("MMC: "); //前面也打印出来了
mmc_initialize(gd->bd);
return 0;
}
int mmc_initialize(bd_t *bis)
{
static int initialized = 0;
int ret;
if (initialized) /* Avoid initializing mmc multiple times */
return 0;
initialized = 1;
#ifndef CONFIG_BLK
#if !CONFIG_IS_ENABLED(MMC_TINY) /* 没定义,要初始化mmc链表 */
mmc_list_init();
#endif
#endif
ret = mmc_probe(bis);
if (ret)
return ret;
#ifndef CONFIG_SPL_BUILD /* 没定义,打印devices */
print_mmc_devices(',');
#endif
mmc_do_preinit();
return 0;
}
void mmc_list_init(void)
{
INIT_LIST_HEAD(&mmc_devices);
cur_dev_num = 0;
}
static int mmc_probe(bd_t *bis)
{
if (board_mmc_init(bis) < 0) //我们初始化完后返回是0,下面的不执行
cpu_mmc_init(bis);
return 0;
}
先简单看一下board_mmc_init函数
int board_mmc_init(bd_t *bis)
{
int i, ret, ret_sd = 0;
/* MASSMEMORY_EN: XMSMDATA7: GPJ2[7] output high */
/* 申请gpiO,并配置为输出模式,属主高电平,越来越像内核了,还要申请,单根尴尬,没判断申请失败的情况...... */
gpio_request(S5PC110_GPIO_J27, "massmemory_en");
gpio_direction_output(S5PC110_GPIO_J27, 1);
/*
* MMC0 GPIO 初始化mmc0 在四线模式下用到的gpio
* GPG0[0] SD_0_CLK
* GPG0[1] SD_0_CMD
* GPG0[2] SD_0_CDn -> Not used
* GPG0[3:6] SD_0_DATA[0:3]
*/
for (i = S5PC110_GPIO_G00; i < S5PC110_GPIO_G07; i++) {
if (i == S5PC110_GPIO_G02)
continue;
/* GPG0[0:6] special function 2 */
gpio_cfg_pin(i, 0x2);
/* GPG0[0:6] pull disable */
gpio_set_pull(i, S5P_GPIO_PULL_NONE);
/* GPG0[0:6] drv 4x */
gpio_set_drv(i, S5P_GPIO_DRV_4X);
}
/* 真正的初始化函数 */
ret = s5p_mmc_init(0, 4); /* 把mmc 0初始化成4线的sdio */
if (ret)
error("MMC: Failed to init MMC:0.\n");
/*
* SD card (T_FLASH) detect and init
* T_FLASH_DETECT: EINT28: GPH3[4] input mode
*/
/* 初始化mmc2 在四线模式下用到的gpio */
gpio_request(S5PC110_GPIO_H34, "t_flash_detect");
gpio_cfg_pin(S5PC110_GPIO_H34, S5P_GPIO_INPUT);
gpio_set_pull(S5PC110_GPIO_H34, S5P_GPIO_PULL_UP);
if (!gpio_get_value(S5PC110_GPIO_H34)) {
for (i = S5PC110_GPIO_G20; i < S5PC110_GPIO_G27; i++) {
if (i == S5PC110_GPIO_G22)
continue;
/* GPG2[0:6] special function 2 */
gpio_cfg_pin(i, 0x2);
/* GPG2[0:6] pull disable */
gpio_set_pull(i, S5P_GPIO_PULL_NONE);
/* GPG2[0:6] drv 4x */
gpio_set_drv(i, S5P_GPIO_DRV_4X);
}
ret_sd = s5p_mmc_init(2, 4); /* 把mmc 0初始化成4线的sdio */
if (ret_sd)
error("MMC: Failed to init SD card (MMC:2).\n");
}
return ret & ret_sd;
}
这个函数是通用的,先得到samsung的mmc的基地址,然后每个mmc通道都是固定的偏移。最后一个传给s5p_sdhci_init
static inline int s5p_mmc_init(int index, int bus_width)
{
unsigned int base = samsung_get_base_mmc() +
(S5P_MMC_DEV_OFFSET * index);
return s5p_sdhci_init(base, index, bus_width);
}
上面的这个samsung_get_base_mmc函数比较好玩,分析一下。直接搜是找不到的。
这边是因为这些外设的格式统一,所以用宏来封装,简化写的重复的的代码量
s5p_cpu_id 这个参数在最前面就已经被赋值过0xC110
#define IS_SAMSUNG_TYPE(type, id) \
static inline int cpu_is_##type(void) \
{ \
return s5p_cpu_id == id ? 1 : 0; \
}
IS_SAMSUNG_TYPE(s5pc100, 0xc100)
IS_SAMSUNG_TYPE(s5pc110, 0xc110)
#define SAMSUNG_BASE(device, base) \
static inline unsigned int samsung_get_base_##device(void) \
{ \
if (cpu_is_s5pc100()) \
return S5PC100_##base; \
else if (cpu_is_s5pc110()) \
return S5PC110_##base; \
else \
return 0; \
}
SAMSUNG_BASE(clock, CLOCK_BASE)
SAMSUNG_BASE(gpio, GPIO_BASE)
SAMSUNG_BASE(pro_id, PRO_ID)
SAMSUNG_BASE(mmc, MMC_BASE)
SAMSUNG_BASE(sromc, SROMC_BASE)
SAMSUNG_BASE(timer, PWMTIMER_BASE)
SAMSUNG_BASE(uart, UART_BASE)
SAMSUNG_BASE(watchdog, WATCHDOG_BASE)
以samsung_get_base_mmc为例分析一下使用
它是下面这展开后的体现
SAMSUNG_BASE(mmc, MMC_BASE)
继续展开SAMSUNG_BASE(mmc, MMC_BASE)对下面的进行替换
#define SAMSUNG_BASE(mmc, MMC_BASE) \
static inline unsigned int samsung_get_base_mmc(void) \
{ \
if (cpu_is_s5pc100()) \
return S5PC100_MMC_BASE; \
else if (cpu_is_s5pc110()) \
return S5PC110_MMC_BASE; \
else \
return 0; \
}
上面的cpu_is_xxx也是用宏定义展开的
#define IS_SAMSUNG_TYPE(type, id) \
static inline int cpu_is_##type(void) \
{ \
return s5p_cpu_id == id ? 1 : 0; \
}
IS_SAMSUNG_TYPE(s5pc100, 0xc100)
IS_SAMSUNG_TYPE(s5pc110, 0xc110)
我们随便展开一个 IS_SAMSUNG_TYPE(s5pc100, 0xc100)
#define IS_SAMSUNG_TYPE(s5pc100, s5pc100) \
static inline int cpu_is_s5pc100(void) \
{ \
return s5p_cpu_id == 0xc100 ? 1 : 0; \
}
因为s5p_cpu_id 在前面被赋值为0xC110,
综上,我们选择为调用samsung_get_base_mmc返回的是当前CPU,S5PC110_MMC_BASE
而这些base都被宏定义了已经如下
/* S5PC100 */
#define S5PC100_PRO_ID 0xE0000000
#define S5PC100_CLOCK_BASE 0xE0100000
#define S5PC100_GPIO_BASE 0xE0300000
#define S5PC100_VIC0_BASE 0xE4000000
#define S5PC100_VIC1_BASE 0xE4100000
#define S5PC100_VIC2_BASE 0xE4200000
#define S5PC100_DMC_BASE 0xE6000000
#define S5PC100_SROMC_BASE 0xE7000000
#define S5PC100_ONENAND_BASE 0xE7100000
#define S5PC100_PWMTIMER_BASE 0xEA000000
#define S5PC100_WATCHDOG_BASE 0xEA200000
#define S5PC100_UART_BASE 0xEC000000
#define S5PC100_MMC_BASE 0xED800000
/* S5PC110 */
#define S5PC110_PRO_ID 0xE0000000
#define S5PC110_CLOCK_BASE 0xE0100000
#define S5PC110_GPIO_BASE 0xE0200000
#define S5PC110_PWMTIMER_BASE 0xE2500000
#define S5PC110_WATCHDOG_BASE 0xE2700000
#define S5PC110_UART_BASE 0xE2900000
#define S5PC110_SROMC_BASE 0xE8000000
#define S5PC110_MMC_BASE 0xEB000000
#define S5PC110_DMC0_BASE 0xF0000000
#define S5PC110_DMC1_BASE 0xF1400000
#define S5PC110_VIC0_BASE 0xF2000000
#define S5PC110_VIC1_BASE 0xF2100000
#define S5PC110_VIC2_BASE 0xF2200000
#define S5PC110_VIC3_BASE 0xF2300000
#define S5PC110_OTG_BASE 0xEC000000
#define S5PC110_PHY_BASE 0xEC100000
#define S5PC110_USB_PHY_CONTROL 0xE010E80C
//三个参数意义分别是,寄存器基址,那个mmc通道,mmc卡初始化成几线宽度数据(我们是4)
int s5p_sdhci_init(u32 regbase, int index, int bus_width)
{
/* 动态申请空间 */
struct sdhci_host *host = calloc(1, sizeof(struct sdhci_host));
if (!host) {
printf("sdhci__host allocation fail!\n");
return -ENOMEM;
}
/* 填充申请的内存空间 */
host->ioaddr = (void *)regbase;
host->index = index;
host->bus_width = bus_width;
return s5p_sdhci_core_init(host);
}
static char *S5P_NAME = "SAMSUNG SDHCI";
//对寄存器的初始化,也是mmc卡初始化核心
static int s5p_sdhci_core_init(struct sdhci_host *host)
{
/* 这个name在上面定义 */
host->name = S5P_NAME; /* 在这里对每个mmc设备绑定了名字 */
/* 初始化寄存器等等 */
host->quirks = SDHCI_QUIRK_NO_HISPD_BIT | SDHCI_QUIRK_BROKEN_VOLTAGE |
SDHCI_QUIRK_32BIT_DMA_ADDR |
SDHCI_QUIRK_WAIT_SEND_CMD | SDHCI_QUIRK_USE_WIDE8;
host->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195;
/* 绑定函数,后面再继续初始化 */
host->set_control_reg = &s5p_sdhci_set_control_reg;
host->set_clock = set_mmc_clk;
if (host->bus_width == 8)
host->host_caps |= MMC_MODE_8BIT;
#ifndef CONFIG_BLK //没定义,要执行
return add_sdhci(host, 52000000, 400000);
#else
return 0;
#endif
}
int add_sdhci(struct sdhci_host *host, u32 max_clk, u32 min_clk)
{
int ret;
/* 寄存器配置 */
ret = sdhci_setup_cfg(&host->cfg, host, max_clk, min_clk);
if (ret)
return ret;
host->mmc = mmc_create(&host->cfg, host);
if (host->mmc == NULL) {
printf("%s: mmc create fail!\n", __func__);
return -ENOMEM;
}
return 0;
}
void print_mmc_devices(char separator)
{
struct mmc *m;
struct list_head *entry;
char *mmc_type;
/* 遍历当前的mmc设备,我们总共注册了两个mmc0 mmc2 */
list_for_each(entry, &mmc_devices) {
m = list_entry(entry, struct mmc, link);
if (m->has_init)
mmc_type = IS_SD(m) ? "SD" : "eMMC";
else
mmc_type = NULL;
/* 在这里打印出前面配置的名字和设备号,我们只有两个,所以是0,1 */
printf("%s: %d", m->cfg->name, m->block_dev.devnum);
if (mmc_type)
printf(" (%s)", mmc_type);
if (entry->next != &mmc_devices) {
printf("%c", separator);
if (separator != '\n')
puts(" ");
}
}
printf("\n");
}
1.14、增加标准输出
int stdio_add_devices(void)
{
#ifdef CONFIG_DM_KEYBOARD /* 没定义键盘 */
struct udevice *dev;
struct uclass *uc;
int ret;
/*
* For now we probe all the devices here. At some point this should be
* done only when the devices are required - e.g. we have a list of
* input devices to start up in the stdin environment variable. That
* work probably makes more sense when stdio itself is converted to
* driver model.
*
* TODO([email protected]): Convert changing uclass_first_device() etc.
* to return the device even on error. Then we could use that here.
*/
ret = uclass_get(UCLASS_KEYBOARD, &uc);
if (ret)
return ret;
/* Don't report errors to the caller - assume that they are non-fatal */
uclass_foreach_dev(dev, uc) {
ret = device_probe(dev);
if (ret)
printf("Failed to probe keyboard '%s'\n", dev->name);
}
#endif
#ifdef CONFIG_SYS_I2C /* 没定义iic */
i2c_init_all();
#else
#if defined(CONFIG_HARD_I2C)
i2c_init (CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE);
#endif
#endif
#ifdef CONFIG_DM_VIDEO /* 没定义图像 */
/*
* If the console setting is not in environment variables then
* console_init_r() will not be calling iomux_doenv() (which calls
* search_device()). So we will not dynamically add devices by
* calling stdio_probe_device().
*
* So just probe all video devices now so that whichever one is
* required will be available.
*/
#ifndef CONFIG_SYS_CONSOLE_IS_IN_ENV //定义了,所以不执行
struct udevice *vdev;
# ifndef CONFIG_DM_KEYBOARD
int ret;
# endif
for (ret = uclass_first_device(UCLASS_VIDEO, &vdev);
vdev;
ret = uclass_next_device(&vdev))
;
if (ret)
printf("%s: Video device failed (ret=%d)\n", __func__, ret);
#endif /* !CONFIG_SYS_CONSOLE_IS_IN_ENV */
#else
# if defined(CONFIG_LCD) /* 没定义 */
drv_lcd_init ();
# endif
# if defined(CONFIG_VIDEO) || defined(CONFIG_CFB_CONSOLE) /* 没定义 */
drv_video_init ();
# endif
#endif /* CONFIG_DM_VIDEO */
#if defined(CONFIG_KEYBOARD) && !defined(CONFIG_DM_KEYBOARD) /* 没定义 */
drv_keyboard_init ();
#endif
#ifdef CONFIG_LOGBUFFER /* 没定义 */
drv_logbuff_init ();
#endif
drv_system_init (); /* 这两个肯定要执行 */
serial_stdio_init ();
#ifdef CONFIG_USB_TTY /* 没定义 */
drv_usbtty_init ();
#endif
#ifdef CONFIG_NETCONSOLE /* 没定义 */
drv_nc_init ();
#endif
#ifdef CONFIG_JTAG_CONSOLE /* 没定义 */
drv_jtag_console_init ();
#endif
#ifdef CONFIG_CBMEM_CONSOLE /* 没定义 */
cbmemc_init();
#endif
return 0;
}
只有两个个函数
第一个,驱动系统初始化,同时注册好标准io
static void drv_system_init (void)
{
struct stdio_dev dev;
memset (&dev, 0, sizeof (dev));
strcpy (dev.name, "serial"); /* 和驱动一样,设备名字很重要 */
dev.flags = DEV_FLAGS_OUTPUT | DEV_FLAGS_INPUT;
dev.putc = stdio_serial_putc;
dev.puts = stdio_serial_puts;
dev.getc = stdio_serial_getc;
dev.tstc = stdio_serial_tstc;
stdio_register (&dev); /* 注册输入,输出 */
#ifdef CONFIG_SYS_DEVICE_NULLDEV /* 没定义 */
memset (&dev, 0, sizeof (dev));
strcpy (dev.name, "nulldev");
dev.flags = DEV_FLAGS_OUTPUT | DEV_FLAGS_INPUT;
dev.putc = nulldev_putc;
dev.puts = nulldev_puts;
dev.getc = nulldev_input;
dev.tstc = nulldev_input;
stdio_register (&dev);
#endif
}
int stdio_register(struct stdio_dev *dev)
{
return stdio_register_dev(dev, NULL);
}
int stdio_register_dev(struct stdio_dev *dev, struct stdio_dev **devp)
{
struct stdio_dev *_dev;
_dev = stdio_clone(dev); /* 前面的是局部变量,这里克隆一份,弄成动态申请的 */
if(!_dev)
return -ENODEV;
list_add_tail(&(_dev->list), &(devs.list)); /* 加入设备链表中 */
if (devp)
*devp = _dev;
return 0;
}
/* 克隆就是动态拷贝一份 */
struct stdio_dev* stdio_clone(struct stdio_dev *dev)
{
struct stdio_dev *_dev;
if(!dev)
return NULL;
_dev = calloc(1, sizeof(struct stdio_dev));
if(!_dev)
return NULL;
memcpy(_dev, dev, sizeof(struct stdio_dev));
return _dev;
}
第二个,注册系统中的所有串口到驱动链表
void serial_stdio_init(void)
{
struct stdio_dev dev;
struct serial_device *s = serial_devices;
while (s) {
memset(&dev, 0, sizeof(dev));
strcpy(dev.name, s->name);
dev.flags = DEV_FLAGS_OUTPUT | DEV_FLAGS_INPUT;
dev.start = serial_stub_start;
dev.stop = serial_stub_stop;
dev.putc = serial_stub_putc;
dev.puts = serial_stub_puts;
dev.getc = serial_stub_getc;
dev.tstc = serial_stub_tstc;
dev.priv = s;
stdio_register(&dev);
s = s->next;
}
}
1.15、initr_jumptable
static int initr_jumptable(void)
{
jumptable_init();
return 0;
}
//初始化跳转表
void jumptable_init(void)
{
gd->jt = malloc(sizeof(struct jt_funcs)); /* 申请跳转表的内存 */
#include <_exports.h> //注意这个头文件在函数里面,所以它里面的内容就是函数的内容
}
展开头文件看一下
/*
* You need to use #ifdef around functions that may not exist
* in the final configuration (such as i2c).
* use a dummyfunction as first parameter to EXPORT_FUNC.
* As an example see the CONFIG_CMD_I2C section below
*/
#ifndef EXPORT_FUNC
#define EXPORT_FUNC(a, b, c, ...)
#endif
EXPORT_FUNC(get_version, unsigned long, get_version, void)
EXPORT_FUNC(getc, int, getc, void)
EXPORT_FUNC(tstc, int, tstc, void)
EXPORT_FUNC(putc, void, putc, const char)
EXPORT_FUNC(puts, void, puts, const char *)
EXPORT_FUNC(printf, int, printf, const char*, ...)
#if (defined(CONFIG_X86) && !defined(CONFIG_X86_64)) || defined(CONFIG_PPC)
EXPORT_FUNC(irq_install_handler, void, install_hdlr,
int, interrupt_handler_t, void*)
EXPORT_FUNC(irq_free_handler, void, free_hdlr, int)
#else
EXPORT_FUNC(dummy, void, install_hdlr, void)
EXPORT_FUNC(dummy, void, free_hdlr, void)
#endif
EXPORT_FUNC(malloc, void *, malloc, size_t)
#if !CONFIG_IS_ENABLED(SYS_MALLOC_SIMPLE)
EXPORT_FUNC(free, void, free, void *)
#endif
EXPORT_FUNC(udelay, void, udelay, unsigned long)
EXPORT_FUNC(get_timer, unsigned long, get_timer, unsigned long)
EXPORT_FUNC(vprintf, int, vprintf, const char *, va_list)
EXPORT_FUNC(do_reset, int, do_reset, cmd_tbl_t *,
int , int , char * const [])
EXPORT_FUNC(getenv, char *, getenv, const char*)
EXPORT_FUNC(setenv, int, setenv, const char *, const char *)
EXPORT_FUNC(simple_strtoul, unsigned long, simple_strtoul,
const char *, char **, unsigned int)
EXPORT_FUNC(strict_strtoul, int, strict_strtoul,
const char *, unsigned int , unsigned long *)
EXPORT_FUNC(simple_strtol, long, simple_strtol,
const char *, char **, unsigned int)
EXPORT_FUNC(strcmp, int, strcmp, const char *cs, const char *ct)
#if defined(CONFIG_CMD_I2C) && \
(!defined(CONFIG_DM_I2C) || defined(CONFIG_DM_I2C_COMPAT))
EXPORT_FUNC(i2c_write, int, i2c_write, uchar, uint, int , uchar * , int)
EXPORT_FUNC(i2c_read, int, i2c_read, uchar, uint, int , uchar * , int)
#else
EXPORT_FUNC(dummy, void, i2c_write, void)
EXPORT_FUNC(dummy, void, i2c_read, void)
#endif
#if !defined(CONFIG_CMD_SPI) || defined(CONFIG_DM_SPI)
EXPORT_FUNC(dummy, void, spi_init, void)
EXPORT_FUNC(dummy, void, spi_setup_slave, void)
EXPORT_FUNC(dummy, void, spi_free_slave, void)
#else
EXPORT_FUNC(spi_init, void, spi_init, void)
EXPORT_FUNC(spi_setup_slave, struct spi_slave *, spi_setup_slave,
unsigned int, unsigned int, unsigned int, unsigned int)
EXPORT_FUNC(spi_free_slave, void, spi_free_slave, struct spi_slave *)
#endif
#ifndef CONFIG_CMD_SPI
EXPORT_FUNC(dummy, void, spi_claim_bus, void)
EXPORT_FUNC(dummy, void, spi_release_bus, void)
EXPORT_FUNC(dummy, void, spi_xfer, void)
#else
EXPORT_FUNC(spi_claim_bus, int, spi_claim_bus, struct spi_slave *)
EXPORT_FUNC(spi_release_bus, void, spi_release_bus, struct spi_slave *)
EXPORT_FUNC(spi_xfer, int, spi_xfer, struct spi_slave *,
unsigned int, const void *, void *, unsigned long)
#endif
EXPORT_FUNC(ustrtoul, unsigned long, ustrtoul,
const char *, char **, unsigned int)
EXPORT_FUNC(ustrtoull, unsigned long long, ustrtoull,
const char *, char **, unsigned int)
EXPORT_FUNC(strcpy, char *, strcpy, char *dest, const char *src)
EXPORT_FUNC(mdelay, void, mdelay, unsigned long msec)
EXPORT_FUNC(memset, void *, memset, void *, int, size_t)
#ifdef CONFIG_PHY_AQUANTIA
EXPORT_FUNC(mdio_get_current_dev, struct mii_dev *,
mdio_get_current_dev, void)
EXPORT_FUNC(phy_find_by_mask, struct phy_device *, phy_find_by_mask,
struct mii_dev *bus, unsigned phy_mask,
phy_interface_t interface)
EXPORT_FUNC(mdio_phydev_for_ethname, struct phy_device *,
mdio_phydev_for_ethname, const char *ethname)
EXPORT_FUNC(miiphy_set_current_dev, int, miiphy_set_current_dev,
const char *devname)
#endif
这里面具体的函数都在examples/standalone/stubs.c文件中
随便找一个分析一下
#define EXPORT_FUNC(f, a, x, ...) \
asm volatile ( \
" .globl " #x "\n" \
#x ":\n" \
" ld $25, %0($26)\n" \
" ld $25, %1($25)\n" \
" jr $25\n" \
: : "i"(offsetof(gd_t, jt)), "i"(FO(x)) : "t9");
参考下面的跳转表的作用,就知道了上面怎么把这些函数放进gd-jt里面的
https://www.oschina.net/question/234345_47573
1.16、console_init_r
struct list_head* stdio_get_list(void)
{
return &(devs.list);
}
static void console_update_silent(void)
{
#ifdef CONFIG_SILENT_CONSOLE
if (getenv("silent") != NULL)
gd->flags |= GD_FLG_SILENT;
else
gd->flags &= ~GD_FLG_SILENT;
#endif
}
int console_init_r(void)
{
struct stdio_dev *inputdev = NULL, *outputdev = NULL;
int i;
struct list_head *list = stdio_get_list(); //得到设备链表
struct list_head *pos;
struct stdio_dev *dev;
console_update_silent(); //我们没有搞什么静默的,所以该输出的还是打印
#ifdef CONFIG_SPLASH_SCREEN //没定义
/*
* suppress all output if splash screen is enabled and we have
* a bmp to display. We redirect the output from frame buffer
* console to serial console in this case or suppress it if
* "silent" mode was requested.
*/
if (getenv("splashimage") != NULL) {
if (!(gd->flags & GD_FLG_SILENT))
outputdev = search_device (DEV_FLAGS_OUTPUT, "serial");
}
#endif
/* Scan devices looking for input and output devices */
//遍历注册的设备链表中,输入和输出都定义了
list_for_each(pos, list) {
dev = list_entry(pos, struct stdio_dev, list);
if ((dev->flags & DEV_FLAGS_INPUT) && (inputdev == NULL)) {
inputdev = dev;
}
if ((dev->flags & DEV_FLAGS_OUTPUT) && (outputdev == NULL)) {
outputdev = dev;
}
if(inputdev && outputdev)
break;
}
/* Initializes output console first,标准输出有了,那标准错误直接用标准输出的就可以,见下面分析 */
if (outputdev != NULL) {
console_setfile(stdout, outputdev);
console_setfile(stderr, outputdev);
#ifdef CONFIG_CONSOLE_MUX
console_devices[stdout][0] = outputdev;
console_devices[stderr][0] = outputdev;
#endif
}
/* Initializes input console */
if (inputdev != NULL) {
console_setfile(stdin, inputdev); //同上
#ifdef CONFIG_CONSOLE_MUX
console_devices[stdin][0] = inputdev;
#endif
}
#ifndef CONFIG_SYS_CONSOLE_INFO_QUIET //这个没定义,要执行
stdio_print_current_devices();
#endif /* CONFIG_SYS_CONSOLE_INFO_QUIET */
/* Setting environment variables,把环境变量最终的标准输入输出错误都名字绑定好 */
for (i = 0; i < 3; i++) {
setenv(stdio_names[i], stdio_devices[i]->name);
}
//标记设备初始化完成
gd->flags |= GD_FLG_DEVINIT; /* device initialization completed */
#if 0
/* If nothing usable installed, use only the initial console */
if ((stdio_devices[stdin] == NULL) && (stdio_devices[stdout] == NULL))
return 0;
#endif
//打印缓冲区里面的信息,我们实际上前面已经打印完了,所以这里不再打印了。
print_pre_console_buffer(PRE_CONSOLE_FLUSHPOINT2_EVERYTHING_BUT_SERIAL);
return 0;
}
1.16.1、console_setfile
static int console_setfile(int file, struct stdio_dev * 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(dev); //这里要初始化一下设备
/* 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->getc = getc;
gd->jt->tstc = tstc;
break;
case stdout:
gd->jt->putc = putc;
gd->jt->puts = puts;
gd->jt->printf = printf;
break;
}
break;
default: /* Invalid file ID */
error = -1;
}
return error;
}
1.16.2、stdio_print_current_devices
//我们的设备名字都是serial,所以下面打印的设备名字都一样
void stdio_print_current_devices(void)
{
/* Print information */
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);
}
}
1.17、interrupt_init
/* IRQ stack memory (calculated at run-time) + 8 bytes */
.globl IRQ_STACK_START_IN
IRQ_STACK_START_IN:
.word 0x0badc0de //编译时保留的空间
int interrupt_init (void)
{
/*
* setup up stacks if necessary
*/
IRQ_STACK_START_IN = gd->irq_sp + 8;
return 0;
}
网络相关的goni默认没配置,所以先不管
终于到大循环了。
static int run_main_loop(void)
{
#ifdef CONFIG_SANDBOX //没定义
sandbox_main_loop_init();
#endif
/* main_loop() can return to retry autoboot, if so just run it again */
for (;;)
main_loop();
return 0;
}