从零开始之七、移植uboot2017.01(七、board_init_r分析)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_16777851/article/details/81782669

上一节已经分析到了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;
}

猜你喜欢

转载自blog.csdn.net/qq_16777851/article/details/81782669