uboot源码分析——start_armboot

来源:互联网 发布:淘宝天龙助手脚本没了 编辑:程序博客网 时间:2024/05/19 18:42


Stage2源码分析

start_armboot函数的路径:/lib_arm/board.c

void start_armboot (void){init_fnc_t **init_fnc_ptr;char *s;#if defined(CONFIG_VFD) || defined(CONFIG_LCD)unsigned long addr;#endif#if defined(CONFIG_MINI2440_LED) struct s3c24x0_gpio * const gpio = s3c24x0_get_base_gpio();#endif/* Pointer is writable since we allocated a register for it */gd = (gd_t*)(_armboot_start - CONFIG_SYS_MALLOC_LEN - sizeof(gd_t));/* compiler optimization barrier needed for GCC >= 3.4 */__asm__ __volatile__("": : :"memory"); memset ((void*)gd, 0, sizeof (gd_t));gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));memset (gd->bd, 0, sizeof (bd_t)); gd->flags |= GD_FLG_RELOC; monitor_flash_len = _bss_start - _armboot_start; for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {if ((*init_fnc_ptr)() != 0) {hang ();}} /* armboot_start is defined in the board-specific linker script */mem_malloc_init (_armboot_start - CONFIG_SYS_MALLOC_LEN,CONFIG_SYS_MALLOC_LEN); #ifndef CONFIG_SYS_NO_FLASH/* configure available FLASH banks */display_flash_config (flash_init ());#endif /* CONFIG_SYS_NO_FLASH */ #ifdef CONFIG_VFD#ifndef PAGE_SIZE#  define PAGE_SIZE 4096#endif/* * reserve memory for VFD display (always full pages) *//* bss_end is defined in the board-specific linker script */addr = (_bss_end + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);vfd_setmem (addr);gd->fb_base = addr;#endif /* CONFIG_VFD */ #ifdef CONFIG_LCD/* board init may have inited fb_base */if (!gd->fb_base) {# ifndef PAGE_SIZE#   define PAGE_SIZE 4096# endif/* * reserve memory for LCD display (always full pages) *//* bss_end is defined in the board-specific linker script */addr = (_bss_end + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);lcd_setmem (addr);gd->fb_base = addr;}#endif /* CONFIG_LCD */ #if defined(CONFIG_CMD_NAND)puts ("NAND:  ");nand_init(); /* go init the NAND */#endif #if defined(CONFIG_CMD_ONENAND)onenand_init();#endif #ifdef CONFIG_HAS_DATAFLASHAT91F_DataflashInit();dataflash_print_info();#endif /* initialize environment */env_relocate (); #ifdef CONFIG_VFD/* must do this after the framebuffer is allocated */drv_vfd_init();#endif /* CONFIG_VFD */ #ifdef CONFIG_SERIAL_MULTIserial_initialize();#endif /* IP Address */gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr"); stdio_init ();/* get the devices list going. */ jumptable_init (); #if defined(CONFIG_API)/* Initialize API */api_init ();#endif console_init_r ();/* fully init console as a device */ #if defined(CONFIG_ARCH_MISC_INIT)/* miscellaneous arch dependent initialisations */arch_misc_init ();#endif#if defined(CONFIG_MISC_INIT_R)/* miscellaneous platform dependent initialisations */misc_init_r ();#endif/* enable exceptions */enable_interrupts (); #ifdef CONFIG_USB_DEVICEusb_init_slave();#endif/* Perform network card initialisation if necessary */#ifdef CONFIG_DRIVER_TI_EMAC/* XXX: this needs to be moved to board init */extern void davinci_eth_set_mac_addr (const u_int8_t *addr);if (getenv ("ethaddr")) {uchar enetaddr[6];eth_getenv_enetaddr("ethaddr", enetaddr);davinci_eth_set_mac_addr(enetaddr);}#endif #if defined(CONFIG_DRIVER_SMC91111) || defined (CONFIG_DRIVER_LAN91C96)/* XXX: this needs to be moved to board init */if (getenv ("ethaddr")) {uchar enetaddr[6];eth_getenv_enetaddr("ethaddr", enetaddr);smc_set_mac_addr(enetaddr);}#endif /* CONFIG_DRIVER_SMC91111 || CONFIG_DRIVER_LAN91C96 */ /* Initialize from environment */if ((s = getenv ("loadaddr")) != NULL) {load_addr = simple_strtoul (s, NULL, 16);}#if defined(CONFIG_CMD_NET)if ((s = getenv ("bootfile")) != NULL) {copy_filename (BootFile, s, sizeof (BootFile));}#endif #ifdef BOARD_LATE_INITboard_late_init ();#endif #ifdef CONFIG_GENERIC_MMCputs ("MMC:   ");mmc_initialize (gd->bd);#endif #ifdef CONFIG_BITBANGMIIbb_miiphy_init();#endif#if defined(CONFIG_CMD_NET)#if defined(CONFIG_NET_MULTI)puts ("Net:   ");#endifeth_initialize(gd->bd);#if defined(CONFIG_RESET_PHY_R)debug ("Reset Ethernet PHY\n");reset_phy();#endif#endif#if defined(CONFIG_MINI2440_LED) gpio->GPBDAT = 0x0; //tekkamanninja#endif #if defined(CONFIG_CFB_CONSOLE)        printf ("%s\n", version_string);printf ("modified by kangear\n(kangear@163.com)\n");printf ("Love Linux forever!!\n");#endif/* main_loop() can return to retry autoboot, if so just run it again. */for (;;) {main_loop ();} /* NOTREACHED - no way out of command loop except booting */}
 

下面来分析代码:

struct s3c24x0_gpio * const gpio = s3c24x0_get_base_gpio();

又有 

static inline struct s3c24x0_gpio *s3c24x0_get_base_gpio(void){return (struct s3c24x0_gpio *)S3C24X0_GPIO_BASE;}
<span style="line-height: 25.98958396911621px; font-size: 12pt; font-family: 宋体;">#define S3C24X0_GPIO_BASE</span><span style="line-height: 25.98958396911621px; font-size: 12pt; font-family: 宋体;"></span><span style="line-height: 25.98958396911621px; font-size: 12pt; font-family: 宋体;">0x56000000</span>

返回返回0x56000000,赋给gpio指针。

 

/* Pointer is writable since we allocated a register for it */gd = (gd_t*)(_armboot_start - CONFIG_SYS_MALLOC_LEN - sizeof(gd_t));

看一下gd_t的定义,在include/asm-arm/global_data.h文件中:

#ifndef__ASM_GBL_DATA_H#define __ASM_GBL_DATA_H/* * The following data structure is placed in some memory wich is * available very early after boot (like DPRAM on MPC8xx/MPC82xx, or * some locked parts of the data cache) to allow for a minimum set of * global variables during system initialization (until we have set * up the memory controller so that we can use RAM). * * Keep it *SMALL* and remember to set CONFIG_SYS_GBL_DATA_SIZE > sizeof(gd_t) */ typedefstructglobal_data {bd_t *bd;unsigned longflags;unsigned longbaudrate;unsigned longhave_console;/* serial_init() was called */unsigned longenv_addr;/* Address  of Environment struct */unsigned longenv_valid;/* Checksum of Environment valid? */unsigned longfb_base;/* base address of frame buffer */#ifdef CONFIG_VFDunsigned charvfd_type;/* display type */#endif#if 0unsigned longcpu_clk;/* CPU clock in Hz! */unsigned longbus_clk;phys_size_tram_size;/* RAM size */unsigned longreset_status;/* reset status register at boot */#endifvoid **jt; /* jump table */} gd_t;

sizeofgd_t)就是gd_t结构体的大小,所以_armboot_start - CONFIG_SYS_MALLOC_LEN - sizeof(gd_t)就相当于减去malloc area再减去gd_t结构体的大小,把此处地址赋值给指针gd

需要注意的一点是

bd_t*bd;

bd是一个结构体指针,指针大小为4字节,并不是该结构体嵌套在gd_t结构体中。

 

/* compiler optimization barrier needed for GCC >= 3.4 */__asm__ __volatile__("": : :"memory");

memory 强制gcc编译器假设RAM所有内存单元均被汇编指令修改,这样cpu中的registerscache中已缓存的内存单元中的数据将作废。cpu将不得不在需要的时候重新读取内存中的数据。这就阻止了cpuregisterscache中的数据用于去优化指令,而避免去访问内存。

 

memset ((void*)gd, 0, sizeof (gd_t));gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));memset (gd->bd, 0, sizeof (bd_t)); gd->flags |= GD_FLG_RELOC; monitor_flash_len = _bss_start - _armboot_start; for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {if ((*init_fnc_ptr)() != 0) {hang ();}}

gd_t结构体进行初始化,首先清0

确定bd_t结构体的位置,并把该位置赋值给指针gd->bd

bd_t结构体清0

gd->flags应该是表示是否要relocate code

#defineGD_FLG_RELOC0x00001 /* Code was relocated to RAM */

 

monitor_flash_len = _bss_start - _armboot_start;

Uboot镜像的大小,以下是一个内存的示意图





for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {if ((*init_fnc_ptr)() != 0) {hang ();}}

board.c中,定义init_sequence

typedef int (init_fnc_t) (void);

又有: 

init_fnc_t *init_sequence[] = {#if defined(CONFIG_ARCH_CPU_INIT)arch_cpu_init, /* basic arch cpu dependent setup */#endifboard_init, /* basic board dependent setup */#if defined(CONFIG_USE_IRQ)interrupt_init, /* set up exceptions */#endiftimer_init, /* initialize timer */env_init, /* initialize environment */init_baudrate, /* initialze baudrate settings */serial_init, /* serial communications setup */console_init_f, /* stage 1 init of console */display_banner, /* say that we are here */#if defined(CONFIG_DISPLAY_CPUINFO)print_cpuinfo, /* display cpu info (and speed) */#endif#if defined(CONFIG_DISPLAY_BOARDINFO)checkboard, /* display board info */#endif#if defined(CONFIG_HARD_I2C) || defined(CONFIG_SOFT_I2C)init_func_i2c,#endifdram_init, /* configure available RAM banks */#if defined(CONFIG_CMD_PCI) || defined (CONFIG_PCI)arm_pci_init,#endifdisplay_dram_config,NULL,};

先是定义一个函数类型typedef int (init_fnc_t) (void);

该函数的返回值int,形参为void

init_sequence为函数指针数组,遍历该数组即执行该数组中所有元素各自指向的函数。


 

下面来看一下指针数组中的各个函数:


arch_cpu_init 

CONFIG_ARCH_CPU_INIT没有找到该定义,则不会执行arch_cpu_init

 

board_init

/board/tekkamanninja/mini2440/mini2440.c中:

/* * Miscellaneous platform dependent initialisations */ int board_init (void){struct s3c24x0_clock_power * const clk_power =s3c24x0_get_base_clock_power();struct s3c24x0_gpio * const gpio = s3c24x0_get_base_gpio(); /* to reduce PLL lock time, adjust the LOCKTIME register */clk_power->LOCKTIME = 0xFFFFFF; /* configure MPLL */clk_power->MPLLCON = ((M_MDIV << 12) + (M_PDIV << 4) + M_SDIV); /* some delay between MPLL and UPLL */delay (4000); /* configure UPLL */clk_power->UPLLCON = ((U_M_MDIV << 12) + (U_M_PDIV << 4) + U_M_SDIV); /* some delay between MPLL and UPLL */delay (8000); /* set up the I/O ports */gpio->GPACON = 0x007FFFFF; #if defined(CONFIG_MINI2440) gpio->GPBCON = 0x00295551;#elsegpio->GPBCON = 0x00044556;#endif gpio->GPBUP = 0x000007FF; #if defined(CONFIG_MINI2440) gpio->GPCCON = 0xAAAAA6AA;gpio->GPCDAT &= ~(1<<5);#elsegpio->GPCCON = 0xAAAAAAAA;#endifgpio->GPCUP = 0xFFFFFFFF;gpio->GPDCON = 0xAAAAAAAA;gpio->GPDUP = 0xFFFFFFFF;     gpio->GPECON = 0xAAAAAAAA;gpio->GPEUP = 0x0000FFFF;gpio->GPFCON = 0x000055AA;gpio->GPFUP = 0x000000FF;gpio->GPGCON = 0xFF95FF3A;gpio->GPGUP = 0x0000FFFF;gpio->GPHCON = 0x0016FAAA;gpio->GPHUP = 0x000007FF; gpio->EXTINT0=0x22222222;gpio->EXTINT1=0x22222222;gpio->EXTINT2=0x22222222; #if defined(CONFIG_S3C2440)/* arch number of S3C2440-Board */gd->bd->bi_arch_number = MACH_TYPE_MINI2440 ;#endif  /* adress of boot parameters */gd->bd->bi_boot_params = 0x30000100; icache_enable();dcache_enable();#ifdefined(CONFIG_MINI2440_LED)gpio->GPBDAT = 0x00000180;#endifreturn 0;}<span style="font-family: 宋体; font-size: 12pt; line-height: 19.5pt;"> </span>

设置时钟,引脚初始化,arch number

gd->bd->bi_boot_params存放参数的地址,这些参数用来启动Linux内核。

I/D Cache 使能。

 

interrupt_init

#if defined(CONFIG_USE_IRQ)interrupt_init, /* set up exceptions */#endif<span style="font-family: 宋体; font-size: 12pt; line-height: 19.5pt;"> </span>

mini2440.h

//#undef CONFIG_USE_IRQ /* we don't need IRQ/FIQ stuff */#define CONFIG_USB_DEVICE 1#ifdef CONFIG_USB_DEVICE#define CONFIG_USE_IRQ 1#endif

所以interrupt_init是执行的:

/lib_arm/interrupts.c

#ifdef CONFIG_USE_IRQDECLARE_GLOBAL_DATA_PTR; int interrupt_init (void){/* * setup up stacks if necessary */IRQ_STACK_START = _armboot_start - CONFIG_SYS_MALLOC_LEN - CONFIG_SYS_GBL_DATA_SIZE - 4;FIQ_STACK_START = IRQ_STACK_START - CONFIG_STACKSIZE_IRQ; return arch_interrupt_init();}

include/configs/mini2440.h文件中:

#ifdef CONFIG_USE_IRQ#define CONFIG_STACKSIZE_IRQ(4*1024)/* IRQ stack */#define CONFIG_STACKSIZE_FIQ(4*1024)/* FIQ stack */#endif

cpu/arm920t/s3c24x0interrupts.c文件中:

int arch_interrupt_init (void){return 0;}

所以这里只是对IRQ_STACK_STARTFIQ_STACK_START这两个宏进行赋值,这两个宏是在哪定义的,看start.S

#ifdef CONFIG_USE_IRQ/* IRQ stack memory (calculated at run-time) */.globl IRQ_STACK_STARTIRQ_STACK_START:.word0x0badc0de /* IRQ stack memory (calculated at run-time) */.globl FIQ_STACK_STARTFIQ_STACK_START:.word 0x0badc0de#endif

之前只是随便赋了一个值0x0badc0de,现在把他们重新赋值而已。

 

timer_init

对定时器的一些初始化,定时器了解的不多,先放这边以后补。。。。

 

env_init

/common/env_nand.c/* this is called before nand_init() * so we can't read Nand to validate env data. * Mark it OK for now. env_relocate() in env_common.c * will call our relocate function which does the real * validation. * * When using a NAND boot image (like sequoia_nand), the environment * can be embedded or attached to the U-Boot image in NAND flash. This way * the SPL loads not only the U-Boot image from NAND but also the * environment. */int env_init(void){#if defined(ENV_IS_EMBEDDED) || defined(CONFIG_NAND_ENV_DST)int crc1_ok = 0, crc2_ok = 0;env_t *tmp_env1; #ifdef CONFIG_ENV_OFFSET_REDUNDenv_t *tmp_env2; tmp_env2 = (env_t *)((ulong)env_ptr + CONFIG_ENV_SIZE);crc2_ok = (crc32(0, tmp_env2->data, ENV_SIZE) == tmp_env2->crc);#endif tmp_env1 = env_ptr; crc1_ok = (crc32(0, tmp_env1->data, ENV_SIZE) == tmp_env1->crc); if (!crc1_ok && !crc2_ok) {gd->env_addr  = 0;gd->env_valid = 0; return 0;} else if (crc1_ok && !crc2_ok) {gd->env_valid = 1;}#ifdef CONFIG_ENV_OFFSET_REDUNDelse if (!crc1_ok && crc2_ok) {gd->env_valid = 2;} else {/* both ok - check serial */if(tmp_env1->flags == 255 && tmp_env2->flags == 0)gd->env_valid = 2;else if(tmp_env2->flags == 255 && tmp_env1->flags == 0)gd->env_valid = 1;else if(tmp_env1->flags > tmp_env2->flags)gd->env_valid = 1;else if(tmp_env2->flags > tmp_env1->flags)gd->env_valid = 2;else /* flags are equal - almost impossible */gd->env_valid = 1;} if (gd->env_valid == 2)env_ptr = tmp_env2;else#endifif (gd->env_valid == 1)env_ptr = tmp_env1; gd->env_addr = (ulong)env_ptr->data; #else /* ENV_IS_EMBEDDED || CONFIG_NAND_ENV_DST */gd->env_addr  = (ulong)&default_environment[0];gd->env_valid = 1;#endif /* ENV_IS_EMBEDDED || CONFIG_NAND_ENV_DST */ return (0);}

本意上是对环境变量进行初始化,但是根据注释可以发现,这个函数是在nand_init之前调用的,所以不可能把Nand中的环境变量CopySDRAM中,这里要做的工作只是对gd结构体的一些变量进行初始化,方便之后的relocation

ENV_IS_EMBEDDEDCONFIG_NAND_ENV_DST都没有被定义。

注意注释:大意是当ubootnand启动时,环境变量有两种存在方式embedded or attached,显然这里不是embeddedENV_IS_EMBEDDED没有被定义),属于attached的形式。

什么是embeddedattached呢?有待研究......

gd->env_addr指向的是环境变量的首地址。

gd->env_valid被赋值为1,在relocate的时候会用到。

来看一下default_environment:

/commom/env_common.c

uchar default_environment[] = {#ifdefCONFIG_BOOTARGS"bootargs="CONFIG_BOOTARGS "\0"#endif#ifdefCONFIG_BOOTCOMMAND"bootcmd="CONFIG_BOOTCOMMAND "\0"#endif#ifdefCONFIG_RAMBOOTCOMMAND"ramboot="CONFIG_RAMBOOTCOMMAND "\0"#endif#ifdefCONFIG_NFSBOOTCOMMAND"nfsboot="CONFIG_NFSBOOTCOMMAND "\0"#endif#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)"bootdelay="MK_STR(CONFIG_BOOTDELAY)"\0"#endif#if defined(CONFIG_BAUDRATE) && (CONFIG_BAUDRATE >= 0)"baudrate="MK_STR(CONFIG_BAUDRATE) "\0"#endif#ifdefCONFIG_LOADS_ECHO"loads_echo="MK_STR(CONFIG_LOADS_ECHO)"\0"#endif#ifdefCONFIG_ETHADDR"ethaddr="MK_STR(CONFIG_ETHADDR) "\0"#endif#ifdefCONFIG_ETH1ADDR"eth1addr="MK_STR(CONFIG_ETH1ADDR) "\0"#endif#ifdefCONFIG_ETH2ADDR"eth2addr="MK_STR(CONFIG_ETH2ADDR) "\0"#endif#ifdefCONFIG_ETH3ADDR"eth3addr="MK_STR(CONFIG_ETH3ADDR) "\0"#endif#ifdefCONFIG_ETH4ADDR"eth4addr="MK_STR(CONFIG_ETH4ADDR) "\0"#endif#ifdefCONFIG_ETH5ADDR"eth5addr="MK_STR(CONFIG_ETH5ADDR) "\0"#endif#ifdefCONFIG_IPADDR"ipaddr="MK_STR(CONFIG_IPADDR) "\0"#endif#ifdefCONFIG_SERVERIP"serverip="MK_STR(CONFIG_SERVERIP) "\0"#endif#ifdefCONFIG_SYS_AUTOLOAD"autoload="CONFIG_SYS_AUTOLOAD "\0"#endif#ifdefCONFIG_PREBOOT"preboot="CONFIG_PREBOOT "\0"#endif#ifdefCONFIG_ROOTPATH"rootpath="MK_STR(CONFIG_ROOTPATH) "\0"#endif#ifdefCONFIG_GATEWAYIP"gatewayip="MK_STR(CONFIG_GATEWAYIP)"\0"#endif#ifdefCONFIG_NETMASK"netmask="MK_STR(CONFIG_NETMASK) "\0"#endif#ifdefCONFIG_HOSTNAME"hostname="MK_STR(CONFIG_HOSTNAME) "\0"#endif#ifdefCONFIG_BOOTFILE"bootfile="MK_STR(CONFIG_BOOTFILE) "\0"#endif#ifdefCONFIG_LOADADDR"loadaddr="MK_STR(CONFIG_LOADADDR) "\0"#endif#ifdef  CONFIG_CLOCKS_IN_MHZ"clocks_in_mhz=1\0"#endif#if defined(CONFIG_PCI_BOOTDELAY) && (CONFIG_PCI_BOOTDELAY > 0)"pcidelay="MK_STR(CONFIG_PCI_BOOTDELAY)"\0"#endif#ifdef  CONFIG_EXTRA_ENV_SETTINGSCONFIG_EXTRA_ENV_SETTINGS#endif"\0"};

default_environment结构体中保存的是默认环境变量,每个环境变量之间以\0隔开,整个结构体以\0\0结尾。



init_baudrate

/lib_arm/board.c文件中:static int init_baudrate (void){char tmp[64];/* long enough for environment variables */int i = getenv_r ("baudrate", tmp, sizeof (tmp));gd->bd->bi_baudrate = gd->baudrate = (i > 0)? (int) simple_strtoul (tmp, NULL, 10): CONFIG_BAUDRATE; return (0);}

在环境变量中查找baudrate字符串,看看有没有设置正确的波特率,有的话就设置上。

 

serial_init

drivers/serial/serial_s3c24x0.c文件中:

#if !defined(CONFIG_SERIAL_MULTI)/* Initialise the serial port. The settings are always 8 data bits, no parity, * 1 stop bit, no start bits. */int serial_init(void){return serial_init_dev(UART_NR);}#endif

同样在这个文件中还有:

/* Initialise the serial port. The settings are always 8 data bits, no parity, * 1 stop bit, no start bits. */static int serial_init_dev(const int dev_index){struct s3c24x0_uart *uart = s3c24x0_get_base_uart(dev_index); #ifdef CONFIG_HWFLOWhwflow = 0;/* turned off by default */#endif /* FIFO enable, Tx/Rx FIFO clear */writel(0x07, &uart->UFCON);writel(0x0, &uart->UMCON); /* Normal,No parity,1 stop,8 bit */writel(0x3, &uart->ULCON);/* * tx=level,rx=edge,disable timeout int.,enable rx error int., * normal,interrupt or polling */writel(0x245, &uart->UCON); #ifdef CONFIG_HWFLOWwritel(0x1, &uart->UMCON);/* RTS up */#endif /* FIXME: This is sooooooooooooooooooo ugly */#if defined(CONFIG_ARCH_GTA02_v1) || defined(CONFIG_ARCH_GTA02_v2)/* we need auto hw flow control on the gsm and gps port */if (dev_index == 0 || dev_index == 1)writel(0x10, &uart->UMCON);#endif_serial_setbrg(dev_index); return (0);}

下面分析代码:

serial_init_dev(UART_NR);中的UART_NR是什么?

找到:

#ifdef CONFIG_SERIAL1#define UART_NRS3C24X0_UART0

/include/s3c2410.h有:

enum s3c24x0_uarts_nr {S3C24X0_UART0,S3C24X0_UART1,S3C24X0_UART2};

枚举类型的第一个成员的默认值是0,所以这里UART_NR的值应该是0,表示对标号为0的串口进行操作。

 

下面看serial_init_dev,他的注释说明了这个函数是用于串口的初始化,数据位为8位,没有奇偶校验(parity),1位停止位,没有起始位。

 s3c24x0_get_base_uart(dev_index);这里相当于 s3c24x0_get_base_uart(0);

include/s3c2410.h中:

static inline struct s3c24x0_uart *s3c24x0_get_base_uart(enum s3c24x0_uarts_nr n){return (struct s3c24x0_uart *)(S3C24X0_UART_BASE + (n * 0x4000));}
<span style="font-family: 宋体; font-size: 12pt; line-height: 25.98958396911621px;">#define S3C24X0_UART_BASE</span><span style="font-family: 宋体; font-size: 12pt; line-height: 25.98958396911621px;"></span><span style="font-family: 宋体; font-size: 12pt; line-height: 25.98958396911621px;">0x50000000</span>

(这里我觉得很奇怪,因为static定义的函数是不能被文件之外的函数调用的)

0x50000000赋值给一个结构体指针,这个结构体类型定义在include/s3c2410.h中:

/* UART (see manual chapter 11) */struct s3c24x0_uart {S3C24X0_REG32ULCON;S3C24X0_REG32UCON;S3C24X0_REG32UFCON;S3C24X0_REG32UMCON;S3C24X0_REG32UTRSTAT;S3C24X0_REG32UERSTAT;S3C24X0_REG32UFSTAT;S3C24X0_REG32UMSTAT;#ifdef __BIG_ENDIANS3C24X0_REG8res1[3];S3C24X0_REG8UTXH;S3C24X0_REG8res2[3];S3C24X0_REG8URXH;#else /* Little Endian */S3C24X0_REG8UTXH;S3C24X0_REG8res1[3];S3C24X0_REG8URXH;S3C24X0_REG8res2[3];#endifS3C24X0_REG32UBRDIV;};

我们看一下芯片手册,刚好是对应起来的。



了解一下writel这个宏,向io寄存器中写入数据,在/include/asm-arm/io.h文件中:

#define writel(v,a) __arch_putl(v,a)#define __arch_putl(v,a) (*(volatile unsigned int *)(a) = (v))

寄存器是4字节,所以这里用int类型对应4字节。

接下来就是对串口相关的寄存器的操作了,不用多说。

 

 

console_init_f

/* Called before relocation - use serial functions */int console_init_f(void){gd->have_console = 1; #ifdef CONFIG_SILENT_CONSOLEif (getenv("silent") != NULL)gd->flags |= GD_FLG_SILENT;#endif return 0;}

主要是对gd结构体进行一些赋值。

 

display_banner

/lib_arm/board.c文件中:

static int display_banner (void){#if defined(CONFIG_MINI2440_LED) struct s3c24x0_gpio * const gpio = s3c24x0_get_base_gpio();gpio->GPBDAT = 0x101; //tekkamanninja#endifprintf ("\n\n%s\n\n", version_string);printf (" modified by kangear (kangear@163.com)\n");printf (" Love Linux forever!!\n\n");debug ("U-Boot code: %08lX -> %08lX  BSS: -> %08lX\n",       _armboot_start, _bss_start, _bss_end);#ifdef CONFIG_MODEM_SUPPORTdebug ("Modem Support enabled\n");#endif#ifdef CONFIG_USE_IRQdebug ("IRQ Stack: %08lx\n", IRQ_STACK_START);debug ("FIQ Stack: %08lx\n", FIQ_STACK_START);#endif return (0);}

gpio->GPBDAT = 0x101; //tekkamanninja

让四个LED灯全亮,打印一些字符串到串口。

Debug执行时要有条件的,必须预先definedebug才会执行。

 

print_cpuinfo

CONFIG_DISPLAY_CPUINFO没有被定义,所以不执行

 

checkboard

没有执行

 

init_func_i2c

/lib_arm/board.c文件中

#if defined(CONFIG_HARD_I2C) || defined(CONFIG_SOFT_I2C)

static int init_func_i2c (void)

{

puts ("I2C:   ");

i2c_init (CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE);

puts ("ready\n");

return (0);

}

#endif

关于i2c的知识有空补充

 

dram_init

/board/tekkamanninja/mini2440/mini2440.c

int dram_init (void){gd->bd->bi_dram[0].start = PHYS_SDRAM_1;gd->bd->bi_dram[0].size = PHYS_SDRAM_1_SIZE; return 0;}

/include/asm-arm/u-boot.h

typedef struct bd_info {    int bi_baudrate;/* serial console baudrate */    unsigned longbi_ip_addr;/* IP Address */    struct environment_s       *bi_env;    ulong        bi_arch_number;/* unique id for this board */    ulong        bi_boot_params;/* where this board expects params */    struct /* RAM configuration */    {ulong start;ulong size;    }bi_dram[CONFIG_NR_DRAM_BANKS];} bd_t;

/include/configs/mini2440.h

#define CONFIG_NR_DRAM_BANKS1   /* we have 1 bank of DRAM */#define PHYS_SDRAM_1 0x30000000 /* SDRAM Bank #1 */#define PHYS_SDRAM_1_SIZE0x04000000 /* 64 MB */

指定了sdram的起始地址和大小。

 

arm_pci_init

不是很了解。

 

display_dram_config

/lib_arm/board.c

static int display_dram_config (void){int i; #ifdef DEBUGputs ("RAM Configuration:\n"); for(i=0; i<CONFIG_NR_DRAM_BANKS; i++) {printf ("Bank #%d: %08lx ", i, gd->bd->bi_dram[i].start);print_size (gd->bd->bi_dram[i].size, "\n");}#elseulong size = 0; for (i=0; i<CONFIG_NR_DRAM_BANKS; i++) {size += gd->bd->bi_dram[i].size;}puts("DRAM:  ");print_size(size, "\n");#endif return (0);}

打印SDRAM的有关信息。

 

init_sequenceNULL结尾。

以上都是

for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {if ((*init_fnc_ptr)() != 0) {hang ();}}

这段代码运行时的分析,我们回到start_armboot中继续往下看

 

 

/* armboot_start is defined in the board-specific linker script */mem_malloc_init (_armboot_start - CONFIG_SYS_MALLOC_LEN,CONFIG_SYS_MALLOC_LEN);

又有/include/configs/mini2440.h

#define CONFIG_SYS_MALLOC_LEN (CONFIG_ENV_SIZE + 128*1024)#define CONFIG_ENV_SIZE 0x20000/* Total Size of Environment Sector */

所以CONFIG_SYS_MALLOC_LEN大小为256K

/common/dmalloc.culong mem_malloc_start = 0;ulong mem_malloc_end = 0;ulong mem_malloc_brk = 0; #ifndef CONFIG_X86/* * x86 boards use a slightly different init sequence thus they implement * their own version of mem_malloc_init() */void mem_malloc_init(ulong start, ulong size){mem_malloc_start = start;mem_malloc_end = start + size;mem_malloc_brk = start; memset((void *)mem_malloc_start, 0, size);}#endif

这里有个疑问,为什么要把CONFIG_ENV_SIZE的大小算在malloc的长度中?

不妨先来假设一下,malloc是用于动态分配内存的,那么环境变量的增加是不是通过动态分配内存,从而保存在sdram中的呢?不确定,带着问题继续向下看。

 

#ifndef CONFIG_SYS_NO_FLASH/* configure available FLASH banks */display_flash_config (flash_init ());#endif /* CONFIG_SYS_NO_FLASH */

如果你是从nand启动的话,那么应该会定义CONFIG_SYS_NO_FLASH,即不会执行display_flash_config函数,如果是从nor启动的话,则会执行:

#ifndef CONFIG_SYS_NO_FLASHstatic void display_flash_config (ulong size){puts ("Flash: ");print_size (size, "\n");}#endif /* CONFIG_SYS_NO_FLASH */
/board/tekkamanninja/mini2440/flash.culong flash_init (void){int i, j;ulong size = 0; for (i = 0; i < CONFIG_SYS_MAX_FLASH_BANKS; i++) {ulong flashbase = 0; flash_info[i].flash_id =#if defined(CONFIG_AMD_LV400)(AMD_MANUFACT & FLASH_VENDMASK) |(AMD_ID_LV400B & FLASH_TYPEMASK);#elif defined(CONFIG_AMD_LV800)(AMD_MANUFACT & FLASH_VENDMASK) |(AMD_ID_LV800B & FLASH_TYPEMASK);#elif defined(CONFIG_SST_VF1601)(SST_MANUFACT & FLASH_VENDMASK) |(SST_ID_xF1601 & FLASH_TYPEMASK);#else#error "Unknown flash configured"#endifflash_info[i].size = FLASH_BANK_SIZE;flash_info[i].sector_count = CONFIG_SYS_MAX_FLASH_SECT;memset (flash_info[i].protect, 0, CONFIG_SYS_MAX_FLASH_SECT);if (i == 0)flashbase = PHYS_FLASH_1;elsepanic ("configured too many flash banks!\n");for (j = 0; j < flash_info[i].sector_count; j++) {#ifndef CONFIG_SST_VF1601if (j <= 3) {/* 1st one is 16 KB */if (j == 0) {flash_info[i].start[j] =flashbase + 0;} /* 2nd and 3rd are both 8 KB */if ((j == 1) || (j == 2)) {flash_info[i].start[j] =flashbase + 0x4000 + (j -1) *0x2000;} /* 4th 32 KB */if (j == 3) {flash_info[i].start[j] =flashbase + 0x8000;}} else {flash_info[i].start[j] =flashbase + (j - 3) * MAIN_SECT_SIZE;}#elseflash_info[i].start[j] =flashbase + (j) * MAIN_SECT_SIZE;#endif }size += flash_info[i].size;} flash_protect (FLAG_PROTECT_SET,       CONFIG_SYS_FLASH_BASE,       CONFIG_SYS_FLASH_BASE + monitor_flash_len - 1,       &flash_info[0]); flash_protect (FLAG_PROTECT_SET,       CONFIG_ENV_ADDR,       CONFIG_ENV_ADDR + CONFIG_ENV_SIZE - 1, &flash_info[0]); return size;}

flash_init确定norflash的型号,然后返回它的大小。

display_flash_config只是打印了norflash的大小,启动时在串口输出:

Flash:   2 MB

 

因为开发板用的是LCD显示屏,所以我们跳过VFD直接看LCD

#ifdef CONFIG_LCD

/* board init may have inited fb_base */

if (!gd->fb_base) {

#ifndef PAGE_SIZE

#  define PAGE_SIZE 4096

#endif

/*

 * reserve memory for LCD display (always full pages)

 */

/* bss_end is defined in the board-specific linker script */

addr = (_bss_end + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);

lcd_setmem (addr);

gd->fb_base = addr;

}

#endif /* CONFIG_LCD */

每页的大小为4K

我们需要某块内存作为frame bufferuboot的机制是将frame buffer放在_bss_end之后。

但是frame buffer的起始地址要稍微处理一下,保证是某一页的页边界,所以采用(_bss_end + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1)的方法进行页对齐。

/common/lcd.c文件下的lcd_setmem函数中看:

/************************************************************************//* ** ROM capable initialization part - needed to reserve FB memory*//************************************************************************//* * This is called early in the system initialization to grab memory * for the LCD controller. * Returns new address for monitor, after reserving LCD buffer memory * * Note that this is running from ROM, so no write access to global data. */ulong lcd_setmem (ulong addr){ulong size;int line_length = (panel_info.vl_col * NBITS (panel_info.vl_bpix)) / 8; debug ("LCD panel info: %d x %d, %d bit/pix\n",panel_info.vl_col, panel_info.vl_row, NBITS (panel_info.vl_bpix) ); size = line_length * panel_info.vl_row; /* Round up to nearest full page */size = (size + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1); /* Allocate pages for the frame buffer. */addr -= size; debug ("Reserving %ldk for LCD Framebuffer at: %08lx\n", size>>10, addr); return (addr);}

返回的addr赋值给gd->fb_base

 

接下来是nandflash的初始化:

#if defined(CONFIG_CMD_NAND)puts ("NAND:  ");nand_init(); /* go init the NAND */#endif/drivers/mtd/nand/nand.c中void nand_init(void){int i;unsigned int size = 0;for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++) {nand_init_chip(&nand_info[i], &nand_chip[i], base_address[i]);size += nand_info[i].size / 1024;if (nand_curr_device == -1)nand_curr_device = i;}printf("%u MiB\n", size / 1024); #ifdef CONFIG_SYS_NAND_SELECT_DEVICE/* * Select the chip in the board/cpu specific driver */board_nand_select_device(nand_info[nand_curr_device].priv, nand_curr_device);#endif}

先了解一下nand_infonand_chip的数据结构:

/drivers/mtd/nand/nand.c

nand_info_t nand_info[CONFIG_SYS_MAX_NAND_DEVICE];

这里CONFIG_SYS_MAX_NAND_DEVICE1.

/include/nand.h

typedef struct mtd_info nand_info_t;/include/linux/mtd/mtd.h中struct mtd_info {u_char type;u_int32_t flags;uint64_t size; /* Total size of the MTD */ /* "Major" erase size for the device. Na飗e users may take this * to be the only erase size available, or may use the more detailed * information below if they desire */u_int32_t erasesize;/* Minimal writable flash unit size. In case of NOR flash it is 1 (even * though individual bits can be cleared), in case of NAND flash it is * one NAND page (or half, or one-fourths of it), in case of ECC-ed NOR * it is of ECC block size, etc. It is illegal to have writesize = 0. * Any driver registering a struct mtd_info must ensure a writesize of * 1 or larger. */u_int32_t writesize; #if defined(ENABLE_CMD_NAND_YAFFS)/*Thanks for hugerat's code*/u_char rw_oob;u_char skipfirstblk;#endif u_int32_t oobsize;   /* Amount of OOB data per block (e.g. 16) */u_int32_t oobavail;  /* Available OOB bytes per block */ /* Kernel-only stuff starts here. */const char *name;int index; /* ecc layout structure pointer - read only ! */struct nand_ecclayout *ecclayout; /* Data for variable erase regions. If numeraseregions is zero, * it means that the whole device has erasesize as given above. */int numeraseregions;struct mtd_erase_region_info *eraseregions; /* * Erase is an asynchronous operation.  Device drivers are supposed * to call instr->callback() whenever the operation completes, even * if it completes with a failure. * Callers are supposed to pass a callback function and wait for it * to be called before writing to the block. */int (*erase) (struct mtd_info *mtd, struct erase_info *instr); /* This stuff for eXecute-In-Place *//* phys is optional and may be set to NULL */int (*point) (struct mtd_info *mtd, loff_t from, size_t len,size_t *retlen, void **virt, phys_addr_t *phys); /* We probably shouldn't allow XIP if the unpoint isn't a NULL */void (*unpoint) (struct mtd_info *mtd, loff_t from, size_t len);  int (*read) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);int (*write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf); /* In blackbox flight recorder like scenarios we want to make successful   writes in interrupt context. panic_write() is only intended to be   called when its known the kernel is about to panic and we need the   write to succeed. Since the kernel is not going to be running for much   longer, this function can break locks and delay to ensure the write   succeeds (but not sleep). */ int (*panic_write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf); int (*read_oob) (struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops);int (*write_oob) (struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops); /* * Methods to access the protection register area, present in some * flash devices. The user data is one time programmable but the * factory data is read only. */int (*get_fact_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);int (*read_fact_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);int (*get_user_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);int (*read_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);int (*write_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);int (*lock_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len); /* XXX U-BOOT XXX */#if 0/* kvec-based read/write methods.   NB: The 'count' parameter is the number of _vectors_, each of   which contains an (ofs, len) tuple.*/int (*writev) (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen);#endif /* Sync */void (*sync) (struct mtd_info *mtd); /* Chip-supported device locking */int (*lock) (struct mtd_info *mtd, loff_t ofs, uint64_t len);int (*unlock) (struct mtd_info *mtd, loff_t ofs, uint64_t len); /* Power Management functions */int (*suspend) (struct mtd_info *mtd);void (*resume) (struct mtd_info *mtd); /* Bad block management functions */int (*block_isbad) (struct mtd_info *mtd, loff_t ofs);int (*block_markbad) (struct mtd_info *mtd, loff_t ofs); /* XXX U-BOOT XXX */#if 0struct notifier_block reboot_notifier;  /* default mode before reboot */#endif /* ECC status information */struct mtd_ecc_stats ecc_stats;/* Subpage shift (NAND) */int subpage_sft; void *priv; struct module *owner;int usecount; /* If the driver is something smart, like UBI, it may need to maintain * its own reference counting. The below functions are only for driver. * The driver may register its callbacks. These callbacks are not * supposed to be called by MTD users */int (*get_device) (struct mtd_info *mtd);void (*put_device) (struct mtd_info *mtd);};

 

nand_chip的数据结构:

static struct nand_chip nand_chip[CONFIG_SYS_MAX_NAND_DEVICE];

CONFIG_SYS_MAX_NAND_DEVICE显然也是1.

/include/linux/mtd/nand.h

struct nand_chip {void  __iomem*IO_ADDR_R;void  __iomem*IO_ADDR_W; uint8_t (*read_byte)(struct mtd_info *mtd);u16 (*read_word)(struct mtd_info *mtd);void (*write_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);void (*read_buf)(struct mtd_info *mtd, uint8_t *buf, int len);int (*verify_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);void (*select_chip)(struct mtd_info *mtd, int chip);int (*block_bad)(struct mtd_info *mtd, loff_t ofs, int getchip);int (*block_markbad)(struct mtd_info *mtd, loff_t ofs);void (*cmd_ctrl)(struct mtd_info *mtd, int dat,    unsigned int ctrl);int (*dev_ready)(struct mtd_info *mtd);void (*cmdfunc)(struct mtd_info *mtd, unsigned command, int column, int page_addr);int (*waitfunc)(struct mtd_info *mtd, struct nand_chip *this);void (*erase_cmd)(struct mtd_info *mtd, int page);int (*scan_bbt)(struct mtd_info *mtd);int (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state, int status, int page);int (*write_page)(struct mtd_info *mtd, struct nand_chip *chip,      const uint8_t *buf, int page, int cached, int raw); int chip_delay;unsigned intoptions; int page_shift;int phys_erase_shift;int bbt_erase_shift;int chip_shift;int numchips;unsigned longchipsize;int pagemask;int pagebuf;int subpagesize;uint8_t cellinfo;int badblockpos; int  state; uint8_t *oob_poi;struct nand_hw_control  *controller;struct nand_ecclayout*ecclayout; struct nand_ecc_ctrl ecc;struct nand_buffers *buffers; struct nand_hw_control hwcontrol; struct mtd_oob_ops ops; uint8_t *bbt;struct nand_bbt_descr*bbt_td;struct nand_bbt_descr*bbt_md; struct nand_bbt_descr*badblock_pattern; void *priv;};

 

还定义了一个base_address

static ulong base_address[CONFIG_SYS_MAX_NAND_DEVICE] = CONFIG_SYS_NAND_BASE_LIST;

/drivers/mtd/nand/nand.c

#ifndef CONFIG_SYS_NAND_BASE_LIST#define CONFIG_SYS_NAND_BASE_LIST { CONFIG_SYS_NAND_BASE }#endif

/include/configs/mini2440.h

#define CONFIG_SYS_NAND_BASE 0x4E000000 

所以base_address0x4E000000,在芯片手册中对应NFCONF


接下来调用nand_init_chip函数,同样是在/drivers/mtd/nand/nand.c

static void nand_init_chip(struct mtd_info *mtd, struct nand_chip *nand,   ulong base_addr){int maxchips = CONFIG_SYS_NAND_MAX_CHIPS;int __attribute__((unused)) i = 0; if (maxchips < 1)maxchips = 1;mtd->priv = nand; nand->IO_ADDR_R = nand->IO_ADDR_W = (void  __iomem *)base_addr;if (board_nand_init(nand) == 0) {if (nand_scan(mtd, maxchips) == 0) {if (!mtd->name)mtd->name = (char *)default_nand_name;#ifndef CONFIG_RELOC_FIXUP_WORKSelsemtd->name += gd->reloc_off;#endif #ifdef CONFIG_MTD_DEVICE/* * Add MTD device so that we can reference it later * via the mtdcore infrastructure (e.g. ubi). */sprintf(dev_name[i], "nand%d", i);mtd->name = dev_name[i++];add_mtd_device(mtd);#endif} elsemtd->name = NULL;} else {mtd->name = NULL;mtd->size = 0;} }

看一下这一句

nand->IO_ADDR_R = nand->IO_ADDR_W = (void  __iomem *)base_addr;

nand_chip结构体中对IO_ADDR_RIO_ADDR_W的定义是:

void  __iomem*IO_ADDR_R;void  __iomem*IO_ADDR_W;

Void类型的指针,看一下网上对__iomem的解释:__iomemlinux2.6.9内核中加入的特性。是用来个表示指针是指向一个I/O的内存空间。主要是为了驱动程序的通用性考虑。由于不同的CPU体系结构对I/O空间的表示可能不同。当使用__iomem时,编译器会忽略对变量的检查(因为用的是void __iomem)。若要对它进行检查,当__iomem的指针和正常的指针混用时,就会发出一些警告。

这两个指针的值都等于0x4E000000.


/drivers/mtd/nand/s3c2410_nand.c

int board_nand_init(struct nand_chip *nand){u_int32_t cfg;u_int8_t tacls, twrph0, twrph1;struct s3c24x0_clock_power *clk_power = s3c24x0_get_base_clock_power();struct s3c2410_nand *nand_reg = s3c2410_get_base_nand(); debugX(1, "board_nand_init()\n"); writel(readl(&clk_power->CLKCON) | (1 << 4), &clk_power->CLKCON); #if defined(CONFIG_S3C2410)/* initialize hardware */twrph0 = 3;twrph1 = 0;tacls = 0; cfg = S3C2410_NFCONF_EN;cfg |= S3C2410_NFCONF_TACLS(tacls - 1);cfg |= S3C2410_NFCONF_TWRPH0(twrph0 - 1);cfg |= S3C2410_NFCONF_TWRPH1(twrph1 - 1);writel(cfg, &nand_reg->NFCONF); /* initialize nand_chip data structure */nand->IO_ADDR_R = nand->IO_ADDR_W = (void *)&nand_reg->NFDATA;#endif#if defined(CONFIG_S3C2440)twrph0 = 4;twrph1 = 2;tacls = 0; cfg = 0;cfg |= S3C2410_NFCONF_TACLS(tacls - 1);cfg |= S3C2410_NFCONF_TWRPH0(twrph0 - 1);cfg |= S3C2410_NFCONF_TWRPH1(twrph1 - 1);writel(cfg, &nand_reg->NFCONF); cfg = (0<<13)|(0<<12)|(0<<10)|(0<<9)|(0<<8)|(0<<6)|(0<<5)|(1<<4)|(0<<1)|(1<<0);writel(cfg, &nand_reg->NFCONT); /* initialize nand_chip data structure */nand->IO_ADDR_R = nand->IO_ADDR_W = (void *)&nand_reg->NFDATA;#endif/* read_buf and write_buf are default *//* read_byte and write_byte are default */ /* hwcontrol always must be implemented */nand->cmd_ctrl = s3c2410_hwcontrol; nand->dev_ready = s3c2410_dev_ready; #ifdef CONFIG_S3C2410_NAND_HWECCnand->ecc.hwctl = s3c2410_nand_enable_hwecc;nand->ecc.calculate = s3c2410_nand_calculate_ecc;nand->ecc.correct = s3c2410_nand_correct_data;nand->ecc.mode = NAND_ECC_HW3_512;#elsenand->ecc.mode = NAND_ECC_SOFT;#endif #ifdef CONFIG_S3C2410_NAND_BBTnand->options = NAND_USE_FLASH_BBT;#elsenand->options = 0;#endif debugX(1, "end of nand_init\n"); return 0;}


 

clk_power指向0x4C000000

 

nand_reg指向0x4E000000



writel(readl(&clk_power->CLKCON) | (1 << 4), &clk_power->CLKCON);

相当于先读取再设值,将CLKCON的标号为4的那一位置1

 

接下来就是对NFCONFNFCONTNFDATA的设置,以及struct nand_chip结构体变量的初始化,比较容易理解。

 

board_nand_init执行完以后会返回0,那么将执行nand_scan

/drivers/mtd/nand/nand_base.c

/** * nand_scan - [NAND Interface] Scan for the NAND device * @mtd:MTD device structure * @maxchips:Number of chips to scan for * * This fills out all the uninitialized function pointers * with the defaults. * The flash ID is read and the mtd/chip structures are * filled with the appropriate values. * The mtd->owner field must be set to the module of the caller * */int nand_scan(struct mtd_info *mtd, int maxchips){int ret; /* Many callers got this wrong, so check for it for a while... *//* XXX U-BOOT XXX */#if 0if (!mtd->owner && caller_is_module()) {printk(KERN_CRIT "nand_scan() called with NULL mtd->owner!\n");BUG();}#endif ret = nand_scan_ident(mtd, maxchips);if (!ret)ret = nand_scan_tail(mtd);return ret;}

执行nand_scan_ident

/drivers/mtd/nand/nand_base.c/** * nand_scan_ident - [NAND Interface] Scan for the NAND device * @mtd:     MTD device structure * @maxchips:     Number of chips to scan for * * This is the first phase of the normal nand_scan() function. It * reads the flash ID and sets up MTD fields accordingly. * * The mtd->owner field must be set to the module of the caller. */int nand_scan_ident(struct mtd_info *mtd, int maxchips){int i, busw, nand_maf_id;struct nand_chip *chip = mtd->priv;struct nand_flash_dev *type; /* Get buswidth to select the correct functions */busw = chip->options & NAND_BUSWIDTH_16;/* Set the default functions */nand_set_defaults(chip, busw); /* Read the flash type */type = nand_get_flash_type(mtd, chip, busw, &nand_maf_id); if (IS_ERR(type)) {#ifndef CONFIG_SYS_NAND_QUIET_TESTprintk(KERN_WARNING "No NAND device found!!!\n");#endifchip->select_chip(mtd, -1);return PTR_ERR(type);} /* Check for a chip array */for (i = 1; i < maxchips; i++) {chip->select_chip(mtd, i);/* See comment in nand_get_flash_type for reset */chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);/* Send the command for reading device ID */chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);/* Read manufacturer and device IDs */if (nand_maf_id != chip->read_byte(mtd) ||    type->id != chip->read_byte(mtd))break;}#ifdef DEBUGif (i > 1)printk(KERN_INFO "%d NAND chips detected\n", i);#endif /* Store the number of chips and calc total size for mtd */chip->numchips = i;mtd->size = i * chip->chipsize; return 0;}

首先

struct nand_chip *chip = mtd->priv;

还记得在nand_init_chip函数中“mtd->priv = nand;”吗?

所以这里的struct nand_chip *chip还是指向在nand_init_chip中定义的static struct nand_chip nand_chip[CONFIG_SYS_MAX_NAND_DEVICE];

接下来定义了一个结构体指针struct nand_flash_dev *type;

/include/linux/mtd/nand.h

/** * struct nand_flash_dev - NAND Flash Device ID Structure * @name:Identify the device type * @id: device ID code * @pagesize:Pagesize in bytes. Either 256 or 512 or 0 * If the pagesize is 0, then the real pagesize * and the eraseize are determined from the * extended id bytes in the chip * @erasesize:Size of an erase block in the flash device. * @chipsize:Total chipsize in Mega Bytes * @options:Bitfield to store chip relevant options */struct nand_flash_dev {char *name;int id;unsigned long pagesize;unsigned long chipsize;unsigned long erasesize;unsigned long options;};

通过int busw;这个变量来表示bus widthbusw = chip->options & NAND_BUSWIDTH_16;

执行nand_set_defaults

/drivers/mtd/nand/nand_base.c

/* * Set default functions */static void nand_set_defaults(struct nand_chip *chip, int busw){/* check for proper chip_delay setup, set 20us if not */if (!chip->chip_delay)chip->chip_delay = 20; /* check, if a user supplied command function given */if (chip->cmdfunc == NULL)chip->cmdfunc = nand_command; /* check, if a user supplied wait function given */if (chip->waitfunc == NULL)chip->waitfunc = nand_wait; if (!chip->select_chip)chip->select_chip = nand_select_chip;if (!chip->read_byte)chip->read_byte = busw ? nand_read_byte16 : nand_read_byte;if (!chip->read_word)chip->read_word = nand_read_word;if (!chip->block_bad)chip->block_bad = nand_block_bad;if (!chip->block_markbad)chip->block_markbad = nand_default_block_markbad;if (!chip->write_buf)chip->write_buf = busw ? nand_write_buf16 : nand_write_buf;if (!chip->read_buf)chip->read_buf = busw ? nand_read_buf16 : nand_read_buf;if (!chip->verify_buf)chip->verify_buf = busw ? nand_verify_buf16 : nand_verify_buf;if (!chip->scan_bbt)chip->scan_bbt = nand_default_bbt; if (!chip->controller) {chip->controller = &chip->hwcontrol; /* XXX U-BOOT XXX */#if 0spin_lock_init(&chip->controller->lock);init_waitqueue_head(&chip->controller->wq);#endif} }

进行一些默认设置,还是针对nand_init_chip中定义的static struct nand_chip nand_chip[CONFIG_SYS_MAX_NAND_DEVICE];

接下来执行nand_scan函数中的nand_scan_tail

/drivers/mtd/nand/nand_base.c

/** * nand_scan_tail - [NAND Interface] Scan for the NAND device * @mtd:    MTD device structure * @maxchips:    Number of chips to scan for * * This is the second phase of the normal nand_scan() function. It * fills out all the uninitialized function pointers with the defaults * and scans for a bad block table if appropriate. */int nand_scan_tail(struct mtd_info *mtd){int i;struct nand_chip *chip = mtd->priv; if (!(chip->options & NAND_OWN_BUFFERS))chip->buffers = kmalloc(sizeof(*chip->buffers), GFP_KERNEL);if (!chip->buffers)return -ENOMEM; /* Set the internal oob buffer location, just after the page data */chip->oob_poi = chip->buffers->databuf + mtd->writesize; /* * If no default placement scheme is given, select an appropriate one */if (!chip->ecc.layout) {switch (mtd->oobsize) {case 8:chip->ecc.layout = &nand_oob_8;break;case 16:chip->ecc.layout = &nand_oob_16;break;case 64:chip->ecc.layout = &nand_oob_64;break;case 128:chip->ecc.layout = &nand_oob_128;break;default:printk(KERN_WARNING "No oob scheme defined for "       "oobsize %d\n", mtd->oobsize);/* BUG(); */}} if (!chip->write_page)chip->write_page = nand_write_page; /* * check ECC mode, default to software if 3byte/512byte hardware ECC is * selected and we have 256 byte pagesize fallback to software ECC */if (!chip->ecc.read_page_raw)chip->ecc.read_page_raw = nand_read_page_raw;if (!chip->ecc.write_page_raw)chip->ecc.write_page_raw = nand_write_page_raw; switch (chip->ecc.mode) {case NAND_ECC_HW_OOB_FIRST:/* Similar to NAND_ECC_HW, but a separate read_page handle */if (!chip->ecc.calculate || !chip->ecc.correct ||     !chip->ecc.hwctl) {printk(KERN_WARNING "No ECC functions supplied, "       "Hardware ECC not possible\n");BUG();}if (!chip->ecc.read_page)chip->ecc.read_page = nand_read_page_hwecc_oob_first; case NAND_ECC_HW:/* Use standard hwecc read page function ? */if (!chip->ecc.read_page)chip->ecc.read_page = nand_read_page_hwecc;if (!chip->ecc.write_page)chip->ecc.write_page = nand_write_page_hwecc;if (!chip->ecc.read_oob)chip->ecc.read_oob = nand_read_oob_std;if (!chip->ecc.write_oob)chip->ecc.write_oob = nand_write_oob_std; case NAND_ECC_HW_SYNDROME:if ((!chip->ecc.calculate || !chip->ecc.correct ||     !chip->ecc.hwctl) &&    (!chip->ecc.read_page ||     chip->ecc.read_page == nand_read_page_hwecc ||     !chip->ecc.write_page ||     chip->ecc.write_page == nand_write_page_hwecc)) {printk(KERN_WARNING "No ECC functions supplied, "       "Hardware ECC not possible\n");BUG();}/* Use standard syndrome read/write page function ? */if (!chip->ecc.read_page)chip->ecc.read_page = nand_read_page_syndrome;if (!chip->ecc.write_page)chip->ecc.write_page = nand_write_page_syndrome;if (!chip->ecc.read_oob)chip->ecc.read_oob = nand_read_oob_syndrome;if (!chip->ecc.write_oob)chip->ecc.write_oob = nand_write_oob_syndrome; if (mtd->writesize >= chip->ecc.size)break;printk(KERN_WARNING "%d byte HW ECC not possible on "       "%d byte page size, fallback to SW ECC\n",       chip->ecc.size, mtd->writesize);chip->ecc.mode = NAND_ECC_SOFT; case NAND_ECC_SOFT:chip->ecc.calculate = nand_calculate_ecc;chip->ecc.correct = nand_correct_data;chip->ecc.read_page = nand_read_page_swecc;chip->ecc.read_subpage = nand_read_subpage;chip->ecc.write_page = nand_write_page_swecc;chip->ecc.read_oob = nand_read_oob_std;chip->ecc.write_oob = nand_write_oob_std;chip->ecc.size = 256;chip->ecc.bytes = 3;break; case NAND_ECC_NONE:printk(KERN_WARNING "NAND_ECC_NONE selected by board driver. "       "This is not recommended !!\n");chip->ecc.read_page = nand_read_page_raw;chip->ecc.write_page = nand_write_page_raw;chip->ecc.read_oob = nand_read_oob_std;chip->ecc.write_oob = nand_write_oob_std;chip->ecc.size = mtd->writesize;chip->ecc.bytes = 0;break; default:printk(KERN_WARNING "Invalid NAND_ECC_MODE %d\n",       chip->ecc.mode);BUG();} /* * The number of bytes available for a client to place data into * the out of band area */chip->ecc.layout->oobavail = 0;for (i = 0; chip->ecc.layout->oobfree[i].length; i++)chip->ecc.layout->oobavail +=chip->ecc.layout->oobfree[i].length;mtd->oobavail = chip->ecc.layout->oobavail; /* * Set the number of read / write steps for one page depending on ECC * mode */chip->ecc.steps = mtd->writesize / chip->ecc.size;if(chip->ecc.steps * chip->ecc.size != mtd->writesize) {printk(KERN_WARNING "Invalid ecc parameters\n");BUG();}chip->ecc.total = chip->ecc.steps * chip->ecc.bytes; /* * Allow subpage writes up to ecc.steps. Not possible for MLC * FLASH. */if (!(chip->options & NAND_NO_SUBPAGE_WRITE) &&    !(chip->cellinfo & NAND_CI_CELLTYPE_MSK)) {switch(chip->ecc.steps) {case 2:mtd->subpage_sft = 1;break;case 4:case 8:mtd->subpage_sft = 2;break;}}chip->subpagesize = mtd->writesize >> mtd->subpage_sft; /* Initialize state */chip->state = FL_READY; /* De-select the device */chip->select_chip(mtd, -1); /* Invalidate the pagebuffer reference */chip->pagebuf = -1; /* Fill in remaining MTD driver data */mtd->type = MTD_NANDFLASH;mtd->flags = MTD_CAP_NANDFLASH;mtd->erase = nand_erase;mtd->point = NULL;mtd->unpoint = NULL;mtd->read = nand_read;mtd->write = nand_write;mtd->read_oob = nand_read_oob;mtd->write_oob = nand_write_oob;mtd->sync = nand_sync;mtd->lock = NULL;mtd->unlock = NULL;mtd->suspend = nand_suspend;mtd->resume = nand_resume;mtd->block_isbad = nand_block_isbad;mtd->block_markbad = nand_block_markbad; /* propagate ecc.layout to mtd_info */mtd->ecclayout = chip->ecc.layout; /* Check, if we should skip the bad block table scan */if (chip->options & NAND_SKIP_BBTSCAN)chip->options |= NAND_BBT_SCANNED; return 0;}

根据注释的意思是将所有空的函数指针进行赋值,检查坏块。

可以看到chipmtd结构体的成员都被进行赋值。

 

跳回nand_init_chip函数,对mtd->name进行设值。

跳回 nand_init函数继续执行

size += nand_info[i].size / 1024;

得到nandflash的大小,单位是KB

nand_curr_device赋值为1.

打印nandflash的大小,256MB,这里是写错了?为什么是MiB......

然后执行board_nand_select_device函数,选中nand芯片,这个函数看不太懂......

nand_init函数执行完了以后向下看,uboot支持onenand,但是我的mini2440只有nornand,所以这里不去研究onenand了。

继续向下看

#ifdef CONFIG_HAS_DATAFLASHAT91F_DataflashInit();dataflash_print_info();#endif

这两句似乎没有执行,先不去管它,随后下debug信息看一下究竟有没有执行。

接下来执行:

/* initialize environment */env_relocate ();/lin_arm/Env_common.cvoid env_relocate (void){#ifndef CONFIG_RELOC_FIXUP_WORKSDEBUGF ("%s[%d] offset = 0x%lx\n", __FUNCTION__,__LINE__,gd->reloc_off);#endif #ifdef CONFIG_AMIGAONEG3SEenable_nvram();#endif #ifdef ENV_IS_EMBEDDED/* * The environment buffer is embedded with the text segment, * just relocate the environment pointer */#ifndef CONFIG_RELOC_FIXUP_WORKSenv_ptr = (env_t *)((ulong)env_ptr + gd->reloc_off);#endifDEBUGF ("%s[%d] embedded ENV at %p\n", __FUNCTION__,__LINE__,env_ptr);#else/* * We must allocate a buffer for the environment */env_ptr = (env_t *)malloc (CONFIG_ENV_SIZE);DEBUGF ("%s[%d] malloced ENV at %p\n", __FUNCTION__,__LINE__,env_ptr);#endif if (gd->env_valid == 0) {#if defined(CONFIG_GTH)|| defined(CONFIG_ENV_IS_NOWHERE)/* Environment not changable */puts ("Using default environment\n\n");#elseputs ("*** Warning - bad CRC, using default environment\n\n");show_boot_progress (-60);#endifset_default_env();}else {env_relocate_spec ();}gd->env_addr = (ulong)&(env_ptr->data); #ifdef CONFIG_AMIGAONEG3SEdisable_nvram();#endif}

CONFIG_RELOC_FIXUP_WORKS定义在/include/asm-arm/config.h

/* Relocation to SDRAM works on all ARM boards */#define CONFIG_RELOC_FIXUP_WORKS

向下看

#ifdef CONFIG_AMIGAONEG3SEenable_nvram();#endif

CONFIG_AMIGAONEG3SE没有被定义,不执行enable_nvram

继续

#ifdef ENV_IS_EMBEDDED/* * The environment buffer is embedded with the text segment, * just relocate the environment pointer */#ifndef CONFIG_RELOC_FIXUP_WORKSenv_ptr = (env_t *)((ulong)env_ptr + gd->reloc_off);#endifDEBUGF ("%s[%d] embedded ENV at %p\n", __FUNCTION__,__LINE__,env_ptr);#else/* * We must allocate a buffer for the environment */env_ptr = (env_t *)malloc (CONFIG_ENV_SIZE);DEBUGF ("%s[%d] malloced ENV at %p\n", __FUNCTION__,__LINE__,env_ptr);#endif

ENV_IS_EMBEDDED没有被定义,执行env_ptr = (env_t *)malloc (CONFIG_ENV_SIZE);

env_ptr是一个结构体指针,全局变量:

extern env_t *env_ptr;

看一下环境变量的结构体:

/include/enviroment.h

typedefstruct environment_s {uint32_tcrc; /* CRC32 over data bytes*/#ifdef CONFIG_SYS_REDUNDAND_ENVIRONMENTunsigned charflags; /* active/obsolete flags*/#endifunsigned chardata[ENV_SIZE]; /* Environment data */} env_t;

又有:

#define ENV_SIZE (CONFIG_ENV_SIZE - ENV_HEADER_SIZE)

ENV_HEADER_SIZE就是crc或者crc+flags

具体看定义:

#ifdef CONFIG_SYS_REDUNDAND_ENVIRONMENT# define ENV_HEADER_SIZE(sizeof(uint32_t) + 1)#else# define ENV_HEADER_SIZE(sizeof(uint32_t))#endif

/include/configs/mini2440.h

#define CONFIG_ENV_SIZE 0x20000/* Total Size of Environment Sector */

接下来判断if (gd->env_valid == 0) 

我们在之前的env_init函数中将gd->env_valid的值设置为1,所以接下来将执行

env_relocate_spec ();

该函数实现真正的重定位功能,先从NAND flash中读取环境变量,如果读取成功,并且crc校验正确的话,就使用NAND flash中读取出来的环境变量,否则使用默认的环境变量。

环境变量的rolocate显然是将环境变量从nor或者nand拷贝到sdram中的相应位置,这里以nand启动为例。

/common/env_nand.c文件中

#ifdef CONFIG_ENV_OFFSET_REDUNDvoid env_relocate_spec (void){#if !defined(ENV_IS_EMBEDDED)int crc1_ok = 0, crc2_ok = 0;env_t *tmp_env1, *tmp_env2; tmp_env1 = (env_t *) malloc(CONFIG_ENV_SIZE);tmp_env2 = (env_t *) malloc(CONFIG_ENV_SIZE); if (readenv(CONFIG_ENV_OFFSET, (u_char *) tmp_env1))puts("No Valid Environment Area Found\n");if (readenv(CONFIG_ENV_OFFSET_REDUND, (u_char *) tmp_env2))puts("No Valid Reundant Environment Area Found\n"); crc1_ok = (crc32(0, tmp_env1->data, ENV_SIZE) == tmp_env1->crc);crc2_ok = (crc32(0, tmp_env2->data, ENV_SIZE) == tmp_env2->crc); if(!crc1_ok && !crc2_ok) {free(tmp_env1);free(tmp_env2);return use_default();} else if(crc1_ok && !crc2_ok)gd->env_valid = 1;else if(!crc1_ok && crc2_ok)gd->env_valid = 2;else {/* both ok - check serial */if(tmp_env1->flags == 255 && tmp_env2->flags == 0)gd->env_valid = 2;else if(tmp_env2->flags == 255 && tmp_env1->flags == 0)gd->env_valid = 1;else if(tmp_env1->flags > tmp_env2->flags)gd->env_valid = 1;else if(tmp_env2->flags > tmp_env1->flags)gd->env_valid = 2;else /* flags are equal - almost impossible */gd->env_valid = 1; } free(env_ptr);if(gd->env_valid == 1) {env_ptr = tmp_env1;free(tmp_env2);} else {env_ptr = tmp_env2;free(tmp_env1);} #endif /* ! ENV_IS_EMBEDDED */}#else /* ! CONFIG_ENV_OFFSET_REDUND *//* * The legacy NAND code saved the environment in the first NAND device i.e., * nand_dev_desc + 0. This is also the behaviour using the new NAND code. */void env_relocate_spec (void){#if !defined(ENV_IS_EMBEDDED)int ret; ret = readenv(CONFIG_ENV_OFFSET, (u_char *) env_ptr);if (ret)return use_default(); if (crc32(0, env_ptr->data, ENV_SIZE) != env_ptr->crc)return use_default();#endif /* ! ENV_IS_EMBEDDED */}#endif /* CONFIG_ENV_OFFSET_REDUND */

CONFIG_ENV_OFFSET_REDUND没有被定义,所以执行第二个env_relocate_spec函数。

这里主要做的工作:

运行readenv函数,从nandCONFIG_ENV_OFFSET位置读去环境变量

如果读取失败,采用默认的环境变量

如果crc校验错误,采用默认的环境变量

先看一下readenv函数

/common/env_nand.c

int readenv (size_t offset, u_char * buf){size_t end = offset + CONFIG_ENV_RANGE;size_t amount_loaded = 0;size_t blocksize, len; u_char *char_ptr; blocksize = nand_info[0].erasesize;len = min(blocksize, CONFIG_ENV_SIZE); while (amount_loaded < CONFIG_ENV_SIZE && offset < end) {if (nand_block_isbad(&nand_info[0], offset)) {offset += blocksize;} else {char_ptr = &buf[amount_loaded];if (nand_read(&nand_info[0], offset, &len, char_ptr))return 1;offset += blocksize;amount_loaded += len;}}if (amount_loaded != CONFIG_ENV_SIZE)return 1; return 0;}

Offset的值为CONFIG_ENV_OFFSET

/include/configs/mini2440.h

#define CONFIG_ENV_OFFSET 0X60000#define CONFIG_ENV_SIZE 0x20000/* Total Size of Environment Sector */

指针buf的值为env_ptr,指向之前分配好的大小为CONFIG_ENV_SIZE大小的内存。

/common/env_nand.c#define CONFIG_ENV_RANGE CONFIG_ENV_SIZE

CONFIG_ENV_RANGE的大小为0x20000

所以end的值为0x80000

然后可以看到通过一个循环,调用nand_read函数从nand中读取环境变量。

这样就完成了从nand中拷贝环境变量。

说明:如果uboot是第一次启动运行env_relocat,这时想要从nandCONFIG_ENV_OFFSET处读取环境变量,但是第一次运行时该位置并没有环境变量!!!

CRC校验时会发现读取的“环境变量”是错误的,那么这时候会使用默认的环境变量。(这时需要输入save命令将环境变量保存到nand中,然后重启就不会出现校验错误了。)

执行use_default();

/common/env_nand.c

#if !defined(ENV_IS_EMBEDDED)static void use_default(){puts ("*** Warning - bad CRC or NAND, using default environment\n\n");set_default_env();}#endif/common/env_common.cvoid set_default_env(void){if (sizeof(default_environment) > ENV_SIZE) {puts ("*** Error - default environment is too large\n\n");return;} memset(env_ptr, 0, sizeof(env_t));memcpy(env_ptr->data, default_environment,       sizeof(default_environment));#ifdef CONFIG_SYS_REDUNDAND_ENVIRONMENTenv_ptr->flags = 0xFF;#endifenv_crc_update ();gd->env_valid = 1;}

首先检验字符数组default_environment的大小,肯定不能比ENV_SIZE大,否则返回。

然后将env_ptr指向的内存清0.

default_environment处的环境变量拷贝到env_ptr->data中,注意,这里的所有操作都是在SDRAM中,不涉及nandSDRAMrelocate

CONFIG_SYS_REDUNDAND_ENVIRONMENT没有定义

运行env_crc_update,更新CRC数据。

/common/env_common.c

void env_crc_update (void){env_ptr->crc = crc32(0, env_ptr->data, ENV_SIZE);}

env_ptr->crc的数据类型是uint32_t,这里通过算法返回一个值,用作下次的CRC校验。具体的crc32算法就不去研究了......

env_relocate_spec执行完毕,跳回env_relocate

然后执行:

gd->env_addr = (ulong)&(env_ptr->data);

还记得在env_init函数中gd->env_addr的设置吗

gd->env_addr  = (ulong)&default_environment[0];

当时是将gd->env_addr设置为默认的default_environment,在完成relocate后就指向了env_ptr->data

 

继续向下看

#ifdef CONFIG_VFD/* must do this after the framebuffer is allocated */drv_vfd_init();#endif /* CONFIG_VFD */ #ifdef CONFIG_SERIAL_MULTIserial_initialize();#endif

这两个函数都不执行。

 

接下来从环境变量中获取IP Address赋值给gd->bd->bi_ip_addr。在网上找了半天,竟然没有getenv_IPaddr的相关说明,索性自己弄个明白。

/* IP Address */gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr");/net/net.cIPaddr_t getenv_IPaddr (char *var){return (string_to_ip(getenv(var)));}

/common/cmd_nvedit.c

/************************************************************************ * Look up variable from environment, * return address of storage for that variable, * or NULL if not found */ char *getenv (char *name){int i, nxt; WATCHDOG_RESET(); for (i=0; env_get_char(i) != '\0'; i=nxt+1) {int val; for (nxt=i; env_get_char(nxt) != '\0'; ++nxt) {if (nxt >= CONFIG_ENV_SIZE) {return (NULL);}}if ((val=envmatch((uchar *)name, i)) < 0)continue;return ((char *)env_get_addr(val));} return (NULL);}

name就是ipaddr

首先要明确一点,getenv最后返回一个字符指针,这个指针肯定指向环境变量ipaddr所在的地址,然后把这个地址返回给string_to_ip函数调用,生成IP地址,然后再返回给gd->bd->bi_ip_addr

现在的问题是,要在众多的环境变量中找出ipaddr

我们已经知道了环境变量的地址:

gd->env_addr = (ulong)&(env_ptr->data);

所以寻找众多环境变量中的一个——ipaddr,肯定要去这个地址进行遍历。

如何遍历呢?

这里通过一个循环的嵌套,fori..)和for(nxt..)

为什么要这么做?想一想一下环境变量在内存中是如何存放的就明白了。

我们之前说过,每个环境变量之间以\0隔开,整个结构体以\0\0结尾。

所以这里的i是用于定位每个环境变量的起始地址,nxt是用于定位i所定位的环境变量的\0的所在地址。你可以把i看做是索引,并且,i是相对于gd->env_addr的索引或者offset,这一点看env_get_char就知道了,后面会讲。

那么 i=nxt+1,对于i来说,就是从当前的环境变量,跳到下一个环境变量,直到i所指向的字符为\0时,到达结构体末尾,遍历完毕。

每次当i定位到某个环境变量时,都会调用envmatch函数来进行比较,看该环境变量是不是ipaddr.

/common/cmd_nv_edit.c

/************************************************************************ * Match a name / name=value pair * * s1 is either a simple 'name', or a 'name=value' pair. * i2 is the environment index for a 'name2=value2' pair. * If the names match, return the index for the value2, else NULL. */int envmatch (uchar *s1, int i2){ while (*s1 == env_get_char(i2++))if (*s1++ == '=')return(i2);if (*s1 == '\0' && env_get_char(i2-1) == '=')return(i2);return(-1);}

理解这个函数的关键是确定等号的位置。

s1有两种情况namename=value 

i2指向的是name2=value2

1namename2=value2

先是while循环,一直到循环结束,if (*s1++ == '=')这个条件都不可能成立。

但是这时if (*s1 == '\0' && env_get_char(i2-1) == '=')是成立的,所以直接返回i2,i2这时候指向的值是等号后面的值,即ip address

2name=valuename2=value2

while (*s1 == env_get_char(i2++))在执行过程中会满足if (*s1++ == '='),直接返回i2.

i2env_get_addr调用

/common/env_common.c

uchar *env_get_addr (int index){if (gd->env_valid) {return ( ((uchar *)(gd->env_addr + index)) );} else {return (&default_environment[index]);}}

因为gd->env_valid是非零(前面设置过),所以返回gd->env_addr + index,并且是转换成uchar的指针形式,指向的正是ip address的值所在的内存。

返回的指针被string_to_ip函数调用最后得到ip地址。

补充一下env_get_char函数

uchar env_get_char (int index){uchar c; /* if relocated to RAM */if (gd->flags & GD_FLG_RELOC)c = env_get_char_memory(index);elsec = env_get_char_init(index); return (c);} uchar env_get_char_memory (int index){if (gd->env_valid) {return ( *((uchar *)(gd->env_addr + index)) );} else {return ( default_environment[index] );}}

本质上和env_get_addr一样,返回值的类型不同而已,都是通过indexgd->env_addr处读取数据。

 

获取完IP地址以后,运行

stdio_init ();/* get the devices list going. *//common/stdio.cint stdio_init (void){#if !defined(CONFIG_RELOC_FIXUP_WORKS)/* 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_RELOC_FIXUP_WORKS */ /* Initialize the list */INIT_LIST_HEAD(&(devs.list)); #ifdef CONFIG_ARM_DCC_MULTIdrv_arm_dcc_init ();#endif#if defined(CONFIG_HARD_I2C) || defined(CONFIG_SOFT_I2C)i2c_init (CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE);#endif#ifdef CONFIG_LCDdrv_lcd_init ();#endif#if defined(CONFIG_VIDEO) || defined(CONFIG_CFB_CONSOLE)drv_video_init ();#endif#ifdef CONFIG_KEYBOARDdrv_keyboard_init ();#endif#ifdef CONFIG_LOGBUFFERdrv_logbuff_init ();#endifdrv_system_init ();#ifdef CONFIG_SERIAL_MULTIserial_stdio_init ();#endif#ifdef CONFIG_USB_TTYdrv_usbtty_init ();#endif#ifdef CONFIG_NETCONSOLEdrv_nc_init ();#endif#ifdef CONFIG_JTAG_CONSOLEdrv_jtag_console_init ();#endif return (0);}
CONFIG_RELOC_FIXUP_WORK是有定义的,所以#endif之前的语句都不执行。运行INIT_LIST_HEAD(&(devs.list));static struct stdio_dev devs;/common/stdio_dev.h/* Device information */struct stdio_dev {intflags; /* Device flags: input/output/system*/intext; /* Supported extensions */charname[16]; /* Device name */ /* GENERAL functions */ int (*start) (void); /* To start the device */int (*stop) (void); /* To stop the device */ /* OUTPUT functions */ void (*putc) (const char c);/* To put a char */void (*puts) (const char *s);/* To put a string (accelerator)*/ /* INPUT functions */ int (*tstc) (void); /* To test if a char is ready...*/int (*getc) (void); /* To get that char */ /* Other functions */ void *priv; /* Private extensions */struct list_head list;};/include/linux/list.hstatic inline void INIT_LIST_HEAD(struct list_head *list){list->next = list;list->prev = list;} struct list_head {struct list_head *next, *prev;};


不难看出devs双向循环链表的一个结点,INIT_LIST_HEAD初始化首结点,使其指向自身。

接下来执行的所有函数,几乎都按照一个流程,创建结点,初始化结点,注册。

我们以其中一个为例:drv_lcd_init

/common/lcd.c

/************************************************************************//* ** GENERIC Initialization Routines *//************************************************************************/ int drv_lcd_init (void){struct stdio_dev lcddev;int rc; lcd_base = (void *)(gd->fb_base); lcd_line_length = (panel_info.vl_col * NBITS (panel_info.vl_bpix)) / 8; lcd_init (lcd_base); /* LCD initialization */ /* Device initialization */memset (&lcddev, 0, sizeof (lcddev)); strcpy (lcddev.name, "lcd");lcddev.ext   = 0; /* No extensions */lcddev.flags = DEV_FLAGS_OUTPUT;/* Output only */lcddev.putc  = lcd_putc; /* 'putc' function */lcddev.puts  = lcd_puts; /* 'puts' function */ rc = stdio_register (&lcddev); return (rc == 0) ? 1 : rc;}

首先定义一个struct stdio_dev lcddev;,这个结构体变量将要作为节点被添加到双向循环链表中。

接下来对lcd进行初始化,以及对结构体lcddev的初始化

最后注册,调用stdio_register函数

/common/stdio.c

int stdio_register (struct stdio_dev * dev){struct stdio_dev *_dev; _dev = stdio_clone(dev);if(!_dev)return -1;list_add_tail(&(_dev->list), &(devs.list));return 0;}

stdio_clone复制dev_dev

调用list_add_tail函数将节点添加到双向循环链表

/include/linux/list.h/** * list_add_tail - add a new entry * @new: new entry to be added * @head: list head to add it before * * Insert a new entry before the specified head. * This is useful for implementing queues. */static inline void list_adlist_addd_tail(struct list_head *new, struct list_head *head){__list_add(new, head->prev, head);}
/include/linux/list.h/* * Insert a new entry between two known consecutive entries. * * This is only for internal list manipulation where we know * the prev/next entries already! */static inline void __list_add(struct list_head *new,      struct list_head *prev,      struct list_head *next){next->prev = new;new->next = next;new->prev = prev;prev->next = new;}

__list_add函数只是负责在prevnext所指向的节点之间插入new节点。

list_add_tail中调用的方式是:__list_add(new, head->prev, head);

表示在head->prev指向的节点和head指向的节点之间添加一个new节点,即在链表的最后添加一个节点。

同样的道理,其他的device也被初始化之后进行注册,stdio_init函数,顾名思义,标准输入输出初始化函数,它做的工作就是这些。

这里是借鉴linux内核中的链表实现方法,只用一个struct list_head结构体就把所有的节点关联起来组成双向链表,相当精彩。

 

继续向下执行jumptable_init

/commom/exports.c

void jumptable_init (void){int i; gd->jt = (void **) malloc (XF_MAX * sizeof (void *));for (i = 0; i < XF_MAX; i++)gd->jt[i] = (void *) dummy; gd->jt[XF_get_version] = (void *) get_version;gd->jt[XF_malloc] = (void *) malloc;gd->jt[XF_free] = (void *) free;gd->jt[XF_getenv] = (void *) getenv;gd->jt[XF_setenv] = (void *) setenv;gd->jt[XF_get_timer] = (void *) get_timer;gd->jt[XF_simple_strtoul] = (void *) simple_strtoul;gd->jt[XF_udelay] = (void *) udelay;gd->jt[XF_simple_strtol] = (void *) simple_strtol;gd->jt[XF_strcmp] = (void *) strcmp;#if defined(CONFIG_I386) || defined(CONFIG_PPC)gd->jt[XF_install_hdlr] = (void *) irq_install_handler;gd->jt[XF_free_hdlr] = (void *) irq_free_handler;#endif/* I386 || PPC */#if defined(CONFIG_CMD_I2C)gd->jt[XF_i2c_write] = (void *) i2c_write;gd->jt[XF_i2c_read] = (void *) i2c_read;#endif#ifdef CONFIG_CMD_SPIgd->jt[XF_spi_init] = (void *) spi_init;gd->jt[XF_spi_setup_slave] = (void *) spi_setup_slave;gd->jt[XF_spi_free_slave] = (void *) spi_free_slave;gd->jt[XF_spi_claim_bus] = (void *) spi_claim_bus;gd->jt[XF_spi_release_bus] = (void *) spi_release_bus;gd->jt[XF_spi_xfer] = (void *) spi_xfer;#endif}

我们把jt叫做jump table,跳转表,本质上是一个二级指针,或者说是一个指针数组,定义了uboot中常用的函数库,jumptable_init函数对这些数组成员进行初始化。

上面的XF_get_version XF_malloc XF_free等在include/exports.h的枚举变量中定义,因此,实际上是作为”Label式整型序号”使用,即XF_get_version=1, XF_malloc=2, XF_free=3

 

继续执行

#if defined(CONFIG_API)/* Initialize API */api_init ();#endif

没有被定义,跳过

 

然后执行

console_init_r ();/* fully init console as a device *//common/console.c/* Called after the relocation - use desired console functions */int console_init_r(void){char *stdinname, *stdoutname, *stderrname;struct stdio_dev *inputdev = NULL, *outputdev = NULL, *errdev = NULL;#ifdef CONFIG_SYS_CONSOLE_ENV_OVERWRITEint i;#endif /* CONFIG_SYS_CONSOLE_ENV_OVERWRITE */#ifdef CONFIG_CONSOLE_MUXint iomux_err = 0;#endif /* set default handlers at first */gd->jt[XF_getc] = serial_getc;gd->jt[XF_tstc] = serial_tstc;gd->jt[XF_putc] = serial_putc;gd->jt[XF_puts] = serial_puts;gd->jt[XF_printf] = serial_printf; /* stdin stdout and stderr are in environment *//* scan for it */stdinname  = getenv("stdin");stdoutname = getenv("stdout");stderrname = getenv("stderr"); if (OVERWRITE_CONSOLE == 0) {/* if not overwritten by config switch */inputdev  = search_device(DEV_FLAGS_INPUT,  stdinname);outputdev = search_device(DEV_FLAGS_OUTPUT, stdoutname);errdev    = search_device(DEV_FLAGS_OUTPUT, stderrname);#ifdef CONFIG_CONSOLE_MUXiomux_err = iomux_doenv(stdin, stdinname);iomux_err += iomux_doenv(stdout, stdoutname);iomux_err += iomux_doenv(stderr, stderrname);if (!iomux_err)/* Successful, so skip all the code below. */goto done;#endif}/* if the devices are overwritten or not found, use default device */if (inputdev == NULL) {inputdev  = search_device(DEV_FLAGS_INPUT,  "serial");}if (outputdev == NULL) {outputdev = search_device(DEV_FLAGS_OUTPUT, "serial");}if (errdev == NULL) {errdev    = search_device(DEV_FLAGS_OUTPUT, "serial");}/* Initializes output console first */if (outputdev != NULL) {/* need to set a console if not done above. */console_doenv(stdout, outputdev);}if (errdev != NULL) {/* need to set a console if not done above. */console_doenv(stderr, errdev);}if (inputdev != NULL) {/* need to set a console if not done above. */console_doenv(stdin, inputdev);} #ifdef CONFIG_CONSOLE_MUXdone:#endif gd->flags |= GD_FLG_DEVINIT;/* device initialization completed */ stdio_print_current_devices(); #ifdef CONFIG_SYS_CONSOLE_ENV_OVERWRITE/* set the environment variables (will overwrite previous env settings) */for (i = 0; i < 3; i++) {setenv(stdio_names[i], stdio_devices[i]->name);}#endif /* CONFIG_SYS_CONSOLE_ENV_OVERWRITE */ #if 0/* If nothing usable installed, use only the initial console */if ((stdio_devices[stdin] == NULL) && (stdio_devices[stdout] == NULL))return 0;#endifreturn 0;}

CONFIG_SYS_CONSOLE_ENV_OVERWRITE没有被定义。

接下来是对jump table的一些默认函数进行设置。

然后执行:

/* stdin stdout and stderr are in environment *//* scan for it */stdinname  = getenv("stdin");stdoutname = getenv("stdout");stderrname = getenv("stderr");

getenv函数之前细致的研究过了,是在众多环境变量中找到需要的环境变量,并返回地址,否则返回NULL注意,这里是在环境变量中查找。

 

OVERWRITE_CONSOLE初始值是0,执行

inputdev  = search_device(DEV_FLAGS_INPUT,  stdinname);outputdev = search_device(DEV_FLAGS_OUTPUT, stdoutname);errdev    = search_device(DEV_FLAGS_OUTPUT, stderrname);

看一下search_device函数

/common/console.c

/** U-Boot INIT FUNCTIONS *************************************************/struct stdio_dev *search_device(int flags, char *name){struct stdio_dev *dev; dev = stdio_get_by_name(name); if (dev && (dev->flags & flags))return dev; return NULL;}

/common/stdio.c

struct stdio_dev* stdio_get_by_name(char* name){struct list_head *pos;struct stdio_dev *dev; if(!name)return NULL; list_for_each(pos, &(devs.list)) {dev = list_entry(pos, struct stdio_dev, list);if(strcmp(dev->name, name) == 0)return dev;} return NULL;}

/include/linux/list.h

/** * list_for_each-iterate over a list * @pos:the &struct list_head to use as a loop cursor. * @head:the head for your list. */#define list_for_each(pos, head) \for (pos = (head)->next; prefetch(pos->next), pos != (head); \pos = pos->next)


pos是一个局部变量,用来遍历从devs.list开始的双向循环链表。至于prefetch,和执行效率有关,么有深究。

看一下list_entry

/common/list.h

/** * list_entry - get the struct for this entry * @ptr:the &struct list_head pointer. * @type:the type of the struct this is embedded in. * @member:the name of the list_struct within the struct. */#define list_entry(ptr, type, member) \container_of(ptr, type, member)

/include/common.h

/** * container_of - cast a member of a structure out to the containing structure * @ptr:the pointer to the member. * @type:the type of the container struct this is embedded in. * @member:the name of the member within the struct. * */#define container_of(ptr, type, member) ({ \const typeof( ((type *)0)->member ) *__mptr = (ptr);\(type *)( (char *)__mptr - offsetof(type,member) );})

/include/linux/stddef.h

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
先看offsetof宏,在理解代码之前,先说一个技巧:

以结构体

struct stdio_dev {.../* Private extensions */struct list_head list;};

为例,假设p是一个指针,指向stdio_dev结构体的某个对象,那么p->list的地址值减去p的值,就是list相对于该结构体的偏移量。

这时,如果强制将p的值设为0,那么

&(struct stdio_dev *0->list)的值就是list相对于该结构体的偏移量。这就是offsetof宏的作用。

再来看container_of宏:

首先定义一个指针__mptr,指针指向的数据类型是((type *)0)->member的数据类型,可能有点拗口,去看一下typeof的用法就知道了。

这个指针的值等于ptrptr的值即pos,即list的实际地址。

这时list的实际地址减去list相对于结构体对象的偏移量,得到的就是该结构体的地址,所以container_of宏返回的是包含member这个成员的结构体变量的地址。

现在来梳理一下,pos是一个临时的struct list_head *类型的指针,用来遍历stdio设备的list,每个stdio设备对应一个struct stdio_dev类型的结构体,

所以遍历list相当于间接的遍历struct stdio_dev结构体组成的双向循环链表,每遍历到一个节点,通过以上的方式返回这个节点的地址,然后获取这个节点的

dev->name,最后将传入的参数namedev->name比较,若是我们要找的stdio device,则返回这个节点的地址,否则返回NULL

 

所以这段代码

inputdev  = search_device(DEV_FLAGS_INPUT,  stdinname);outputdev = search_device(DEV_FLAGS_OUTPUT, stdoutname);errdev    = search_device(DEV_FLAGS_OUTPUT, stderrname);<span style="white-space:pre"></span>

执行后inputdevoutputdeverrdev分别是,struct stdio_dev结构体组成的双向循环链表中代表stdinstdoutstderr含义的结构体的地址。

CONFIG_CONSOLE_MUX没有被定义。

接下来是检验inputdevoutputdeverrdev是否为空,如果是NULL的话,就以另一种方式来 search_device,总之确保inputdev不为空。

/common/console.c

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();/* 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[XF_getc] = dev->getc;gd->jt[XF_tstc] = dev->tstc;break;case stdout:gd->jt[XF_putc] = dev->putc;gd->jt[XF_puts] = dev->puts;gd->jt[XF_printf] = printf;break;}break; default: /* Invalid file ID */error = -1;}return error;}

/include/common.h

#define stdin 0#define stdout 1#define stderr 2

drv_system_init下没定义函数dev->start

stdio_devices[file] = dev;dev放入到/common/stdio.c文件中定义的stdio_devices结构体数组中。

struct stdio_dev *stdio_devices[] = { NULL, NULL, NULL };

接下来是对jump table函数指针的设置。

 

跳回console_init_r函数继续执行,

设置gd->flags

执行stdio_print_current_devices();

/common/console.c

void stdio_print_current_devices(void){#ifndef CONFIG_SYS_CONSOLE_INFO_QUIET/* 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);}#endif /* CONFIG_SYS_CONSOLE_INFO_QUIET */}

在串口上打印一些信息,uboot启动的时候就会打印:

In:    serial

Out:   serial

Err:   serial

 

然后执行:

/* set the environment variables (will overwrite previous env settings) */for (i = 0; i < 3; i++) {setenv(stdio_names[i], stdio_devices[i]->name);}

/common/stdio.h

struct stdio_dev *stdio_devices[] = { NULL, NULL, NULL };

char *stdio_names[MAX_FILES] = { "stdin", "stdout", "stderr" };

Setenv这个函数太过复杂......之后讲到命令的时候在研究吧......

 

回到start_armboot

CONFIG_ARCH_MISC_INI没有被定义,misc_init_r不执行。

 

接下来执行:

/* enable exceptions */

enable_interrupts ();

/lib_arm/interrupts.c

/* enable IRQ interrupts */void enable_interrupts (void){unsigned long temp;__asm__ __volatile__("mrs %0, cpsr\n"     "bic %0, %0, #0x80\n"     "msr cpsr_c, %0"     : "=r" (temp)     :     : "memory");}

用一段内嵌汇编代码将CPSR寄存器中的禁止/使能中断位清零,也就是打开了中断.

 

接下来执行

#ifdef CONFIG_USB_DEVICEusb_init_slave();#endif

/include/configs/mini2440.h

#define CONFIG_USB_DEVICE 1

Usb方面的知识不熟,之后再补吧。

 

CONFIG_DRIVER_TI_EMAC没有被定义。

 

CONFIG_DRIVER_SMC91111CONFIG_DRIVER_LAN91C96都没有被定义

 

然后执行

/* Initialize from environment */if ((s = getenv ("loadaddr")) != NULL) {load_addr = simple_strtoul (s, NULL, 16);}

获取loadaddr对应的环境变量,然后转换成unsigned long类型。

unsigned long long simple_strtoull(const char *cp, char **endp, unsigned int base)是一个常用的底层函数,不去深究了。
功能:将一个字符串转换成unsigend long long型数据。
返回:返回转换后数据。

参数:cp指向字符串的开始,endp指向分析的字符串末尾的位置,base为要用的基数(进制数),base0表示通过cp来自动判断基数,函数自动可识别的基数:‘0x’表示16进制,‘0’表示8进制,其它都认定为10进制。函数可转换成数字的有效字符为:[0f]

 

接下来执行

#if defined(CONFIG_CMD_NET)if ((s = getenv ("bootfile")) != NULL) {copy_filename (BootFile, s, sizeof (BootFile));}#endif/include/config_cmd_default.h#define CONFIG_CMD_NET /* bootp, tftpboot, rarpboot*//net/net.cchar BootFile[128]; /* Boot File name *//net/net.cvoid copy_filename (char *dst, char *src, int size){if (*src && (*src == '"')) {++src;--size;} while ((--size > 0) && *src && (*src != '"')) {*dst++ = *src++;}*dst = '\0';}

获得文件名,并把这些文件名赋值给BootFile字符数组。

 

BOARD_LATE_INIT没有定义,board_late_init没有实现。

 

CONFIG_GENERIC_MMC没有定义。

 

CONFIG_BITBANGMII没有定义。

 

接下来是对网卡的初始化,网卡没接触过,没有深入研究这段代码。

#if defined(CONFIG_CMD_NET)#if defined(CONFIG_NET_MULTI)puts ("Net:   ");#endifeth_initialize(gd->bd);#if defined(CONFIG_RESET_PHY_R)debug ("Reset Ethernet PHY\n");reset_phy();#endif#endif #if defined(CONFIG_MINI2440_LED) gpio->GPBDAT = 0x0; //tekkamanninja#endif

点个灯而已。

 

#if defined(CONFIG_CFB_CONSOLE)        printf ("%s\n", version_string);printf ("modified by kangear\n(kangear@163.com)\n");printf ("Love Linux forever!!\n");#endif

打印一些信息

version_string/lib_arm/board.c中有定义

const char version_string[] =U_BOOT_VERSION" (" U_BOOT_DATE " - " U_BOOT_TIME ")"CONFIG_IDENT_STRING;

 

最后是一个死循环,运行main_loop ();

/* main_loop() can return to retry autoboot, if so just run it again. */for (;;) {main_loop ();}




 












0 0
原创粉丝点击