從零開始之uboot、移植uboot2017.01(七、board_init_r分析)
上一節已經分析到了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裡面的
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;
}