start_armboot()是U-BOOT为2440的启动是初始化硬件,引导挂载linux系统的核心代码,看起来还是有一点复杂的,边学习变记笔记,这里主要讲的还是和2440有关的代码,去除了一些条件编译的没有用到的代码,有什么不对的地方欢迎指正。
init_fnc_t **init_fnc_ptr;
char *s
char buf1[10];
char buf2[15];
buff1与buff2为U-BOOT 和板子的名称,不同板子有些差异,这里不用管。
init_fnc_t 的声明为
typedef int (init_fnc_t) (void);
表示 int (void)类型的函数类型进行取别名init_fnc_t,在同一个文件下有一个全局变量*init_sequence[]内容为
init_fnc_t *init_sequence[] = {
cpu_init, /* basic cpu dependent setup */
board_init, /* basic board dependent setup */
interrupt_init, /* set up exceptions */
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
dram_init, /* configure available RAM banks */
display_dram_config,
NULL,
};
init_fnc_t *init_sequence[]定义的是一个函数指针数组,cpu_init、board_init等这些为相应函数的地址,分别被指向init_sequenc[0],init_sequenc[1]……等,char *s定义了一个char 类型的指针变量,后面传参数会频繁用到。接下来程序为
gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t));
gd_t是一个看起来比较复杂的结构体,结构体具体包括另一个 bd_t结构体,bd_t中还包括environment_s结构体
typedef struct global_data {
bd_t *bd;
unsigned long flags;
unsigned long baudrate; /*波特率*/
unsigned long have_console; /* serial_init() was called */
unsigned long reloc_off; /* Relocation Offset */
unsigned long env_addr; /* Address of Environment struct */
unsigned long env_valid; /* Checksum of Environment valid? */
unsigned long fb_base; /* base address of frame buffer */
void **jt; /* jump table */
} gd_t;
gd_t 结构体
typedef struct bd_info {
int bi_baudrate; /* serial console baudrate */
unsigned long bi_ip_addr; /* IP Address */
unsigned char bi_enetaddr[6]; /* Ethernet adress */
struct environment_s *bi_env;
ulong bi_arch_number; /* unique id for this board */
ulong bi_boot_params; /* where this board expects params */
struct /* RAM configuration */
{
ulong start;
ulong size;
} bi_dram[CONFIG_NR_DRAM_BANKS];
#endif
} bd_t;
bd_t结构体
typedef struct environment_s {
ulong crc; /* CRC32 over data bytes */
uchar flags; /* active or obsolete */
uchar *data;
} env_t;
env_t结构体
gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t));//讲gd指向固定的位置
gd在宏定义DECLARE_GLOBAL_DATA_PT中,在global_data.h中有定义,这个定义表示gd占用的是r8寄存器,
#define DECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm ("r8")
在U-BOOT的内存分布中,_armboot_start地址为_start,在根目录的Makefile和board的链接文件中表明了_armboot_start为U-BOOT存放的起始地址空间(_TEXT_BASE),如图可以看到gd位于的位置在CFG_GBL_DATA_SIZE中, CFG_MALLOC_LEN代表的是堆区域。
__asm__ __volatile__("": : :"memory");
__asm__表示告诉编译器此处插入汇编指令。
__volatile__表示告诉编译器不允许讲该句代码优化编译。
“”: : :"memory"该命令用来欺骗gcc工具链,告诉编译器之前所有内存单元都修改过了,让编译器抛弃之前在registers和cache中已缓存的内存单元中的数据,并之后重新读取那些数据,也是防止优化作用。
memset ((void*)gd, 0, sizeof (gd_t));
gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));
memset (gd->bd, 0, sizeof (bd_t));
给gd和gd->bd分别初始化内存,因为是都是指正变量必须要分配存储空间
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
if ((*init_fnc_ptr)() != 0) {
hang ();//分别调用函数,执行错误打印错误信息
}
}
调用函数如下:
int cpu_init (void)
{
IRQ_STACK_START = _armboot_start - CFG_MALLOC_LEN - CFG_GBL_DATA_SIZE - 4;
FIQ_STACK_SDFGBSTART = IRQ_STACK_START - CONFIG_STACKSIZE_IRQ;
FREE_RAM_END = FIQ_STACK_START - CONFIG_STACKSIZE_FIQ - CONFIG_STACKSIZE;
FREE_RAM_SIZE = FREE_RAM_END - PHYS_SDRAM_1;
return 0;
}
cpu_init (void)的主要目的目的是给快速中断和慢速中断分配内存空间,PHYS_SDRAM_1一般为使用SDRAM的起始地址,在S3C2440中为0x30000000。还计算除了系统的空间内存。
int board_init (void)
{
S3C24X0_CLOCK_POWER * const clk_power = S3C24X0_GetBase_CLOCK_POWER();
S3C24X0_GPIO * const gpio = S3C24X0_GetBase_GPIO();
/* set up the I/O ports */
gpio->GPACON = 0x007FFFFF;
gpio->GPBCON = 0x00055555;
gpio->GPBUP = 0x000007FF;
gpio->GPCCON = 0xAAAAAAAA;
gpio->GPCUP = 0x0000FFFF;
gpio->GPDCON = 0xAAAAAAAA;
gpio->GPDUP = 0x0000FFFF;
gpio->GPECON = 0xAAAAAAAA;
gpio->GPEUP = 0x0000FFFF;
gpio->GPFCON = 0x000055AA;
gpio->GPFUP = 0x000000FF;
gpio->GPGCON = 0xFF94FFBA;
gpio->GPGUP = 0x0000FFEF;
gpio->GPGDAT = gpio->GPGDAT & (~(1<<4)) | (1<<4) ;
gpio->GPHCON = 0x002AFAAA;
gpio->GPHUP = 0x000007FF;
gpio->GPJCON = 0x02aaaaaa;
gpio->GPJUP = 0x00001fff;
gd->bd->bi_arch_number = MACH_TYPE_S3C2440;
gd->bd->bi_boot_params = 0x30000100;
icache_enable();
dcache_enable();
return 0;
}
该函数首先使用时钟寄存器结构体和Gpio寄存器结构体获取了对应的地址,在初始化gpio外设 ,在gd->bd->bi_arch_number中保存了开发板子的机器ID,gd->bd->bi_boot_params = 0x30000100;表示传递给内核的参数的位置,最后就是打开Icache和dcache。
int interrupt_init (void)
{
S3C24X0_TIMERS * const timers = S3C24X0_GetBase_TIMERS();
/* use PWM Timer 4 because it has no output */
/* prescaler for Timer 4 is 16 */
timers->TCFG0 = 0x0f00;
if (timer_load_val == 0)
{
timer_load_val = get_PCLK()/(2 * 16 * 100);
}
/* load value for 10 ms timeout */
lastdec = timers->TCNTB4 = timer_load_val;
/* auto load, manual update of Timer 4 */
timers->TCON = (timers->TCON & ~0x0700000) | 0x600000;
/* auto load, start Timer 4 */
timers->TCON = (timers->TCON & ~0x0700000) | 0x500000;
timestamp = 0;
return (0);
}
首先获取PWM有关寄存器的地址,设置预分频数,TCFG0 寄存器中设置参数0x0f00设置预分频系数为16,在汇编层面的代码中将CPU的频率设置在200Mhz,采用FCLK:HCLK:PCLK为1:2:4,在这里可以得到PCLK的时钟频率为50Hhz
timer_load_val = get_PCLK()/(2 * 16 * 100);
/* load value for 10 ms timeout */
lastdec = timers->TCNTB4 = timer_load_val;
get_PCLK获取到PCLK的频率,prescaler = 15,dicider = 2,所以得到的定时器的时钟为PCLK/((15+1)*2),系统目标是得到10ms的时钟,所以用timer_load_val 来表示重新加载的数值。
env_init();//环境变量初始化
接下来说比较复杂的U-boot环境变量的初始化,环境变量是可以对软件全局配置的信息,可以在非已失性存储器上永久保存,可以被CPU查寻,读取的变量,CPU在上电的时候正是利用环境变量进行初始化。环境变量是存储在flsah上的,也就是说在使用norflahs或者nandflash启动的时候才会有效。
在\u-boot\include\configs\board_name.h下设置了关于环境变量存储的参数
//#define CFG_ENV_IS_IN_FLASH 1
#define CFG_ENV_IS_IN_NAND 1
#define CFG_ENV_OFFSET 0x40000
#define CFG_ENV_SIZE64 0xc000 /* Total Size of Environment Sector */
#if(CONFIG_64MB_Nand == 1)
#define CFG_ENV_SIZE 0xc000 /* Total Size of Environment Sector */
相关的全局变量保存在common/environment.c中。
现在开始看env_init();的代码,由于在配置文件下 #define CFG_ENV_IS_IN_NAND 1 故使用Nandflash启动,env_init代码保存在\u-boot\common\env_nand.c中
int env_init(void)
{
#if defined(ENV_IS_EMBEDDED)
ulong total;
int crc1_ok = 0, crc2_ok = 0;
env_t *tmp_env1, *tmp_env2;
total = CFG_ENV_SIZE;
tmp_env1 = env_ptr;//env_t *env_ptr = (env_t *)(&environment[0]);
tmp_env2 = (env_t *)((ulong)env_ptr + CFG_ENV_SIZE);
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)
gd->env_valid = 0;
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;
}
if (gd->env_valid == 1)
env_ptr = tmp_env1;
else if (gd->env_valid == 2)
env_ptr = tmp_env2;
#else /* ENV_IS_EMBEDDED */
gd->env_addr = (ulong)&default_environment[0];
gd->env_valid = 1;
#endif /* ENV_IS_EMBEDDED */
return (0);
}
tmp_env1 指向环境变量的开头,tmp_env2指向环境变量的结束,接着就是CRC校验,通过判断校验位来确定环境变量的最终指向,但是在\u-boot\u-boot-1.1.6\include\environment.h中ENV_IS_EMBEDDED未定义,所以直接跳转到else执行,表示环境变量指向系统默认位置。
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);
}
init_baudrate ()设置波特率,getenv_r ()是用来读取环境变量的函数,其函数本身为
int getenv_r (char *name, char *buf, unsigned len)
{
int i, nxt;
for (i=0; env_get_char(i) != '\0'; i=nxt+1) {
int val, n;
for (nxt=i; env_get_char(nxt) != '\0'; ++nxt) {
if (nxt >= CFG_ENV_SIZE) {
return (-1);
}
}
if ((val=envmatch((uchar *)name, i)) < 0)
continue;
/* found; copy out */
n = 0;
while ((len > n++) && (*buf++ = env_get_char(val++)) != '\0')
;
if (len == n)
*buf = '\0';
return (n);
}
return (-1);
}
在getenv_r()使用了env_get_char(int index)和envmatch(uchar *s1, int i2)函数,env_get_char使用index号来遍历环境变量的值,envmatch使用s1也就是环境变量名来遍历环境变量来得到它的值,init_baudrate()的作用就是将环境变量的值取出来,赋值给gd结构体
void serial_setbrg (void)
{
S3C24X0_UART * const uart = S3C24X0_GetBase_UART(UART_NR);
int i;
unsigned int reg = 0;
/* value is calculated so : (int)(PCLK/16./baudrate) -1 */
reg = get_PCLK() / (16 * gd->baudrate) - 1;
/* FIFO enable, Tx/Rx FIFO clear */
uart->UFCON = 0x07;
uart->UMCON = 0x0;
/* Normal,No parity,1 stop,8 bit */
uart->ULCON = 0x3;
/*
* tx=level,rx=edge,disable timeout int.,enable rx error int.,
* normal,interrupt or polling
*/
uart->UCON = 0x245;
uart->UBRDIV = reg;
#ifdef CONFIG_HWFLOW
uart->UMCON = 0x1; /* RTS up */
#endif
for (i = 0; i < 100; i++);
}
serial_init()为初始化串口函数,设置串口的工作模式
int console_init_f (void)
{
gd->have_console = 1;
#ifdef CONFIG_SILENT_CONSOLE
if (getenv("silent") != NULL)
gd->flags |= GD_FLG_SILENT;
#endif
return (0);
}
console_init_f()作用是控制台的前期初始化
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;
}
dram_init ();在U-boot中初始化ram
init_fnc_ptr()到现在就介绍完了。
static ulong mem_malloc_start = 0;
static ulong mem_malloc_end = 0;
static ulong mem_malloc_brk = 0;
static
void mem_malloc_init (ulong dest_addr)
{
mem_malloc_start = dest_addr;
mem_malloc_end = dest_addr + CFG_MALLOC_LEN;
mem_malloc_brk = mem_malloc_start;
memset ((void *) mem_malloc_start, 0,
mem_malloc_end - mem_malloc_start);
}
mem_malloc_init (_armboot_start - CFG_MALLOC_LEN);的目的是给堆区分配内存,memset()根据设定的内存大小和内存地址将这一块内存数据清零。
void nand_init(void)
{
int i;
unsigned int size = 0;
for (i = 0; i < CFG_MAX_NAND_DEVICE; i++) {
nand_init_chip(&nand_info[i], &nand_chip[i], base_address[i]);
size += nand_info[i].size;
if (nand_curr_device == -1)
nand_curr_device = i;
}
nand_info数组的结构体类型为mtd_info (nand_info_t) mtd为内存技术驱动程序,简单介绍一些MTD的专用名词,CFI:通用FLash接口、OBB:某些内存技术支持out-of-band数据,入NAND每512的块有16个字节的extra data 用于纠错或元数据、ECC有些硬件不仅允许对flash 进行访问,还具有ECC功功能,所以flash器件都会受到交换位现象的困扰,在某些情况下,一个比特位会发生反转或者被报告反转了,就要采用ECC算法
struct mtd_info {
u_char type;//内存技术的类型
u_int32_t flags;//标志位
u_int32_t size; //MTD设备的大小
/* "Major" erase size for the device. Na�ve 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;//主要擦除块的大小
u_int32_t oobblock; /* Size of OOB blocks (e.g. 512) 块大小*/
u_int32_t oobsize; /* Amount of OOB data per block (e.g. 16) 数据大小*/
u_int32_t oobavail; /* Number of bytes in OOB area available for fs */
u_int32_t ecctype;//ECC类型
u_int32_t eccsize;//ECC工作范围
/* Kernel-only stuff starts here. */
char *name;
int index;
/* oobinfo is a nand_oobinfo structure, which can be set by iotcl (MEMSETOOBINFO) */
struct nand_oobinfo oobinfo;
/* 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;
/* This really shouldn't be here. It can go away in 2.5 */
u_int32_t bank_size;
int (*erase) (struct mtd_info *mtd, struct erase_info *instr);
/* This stuff for eXecute-In-Place */
int (*point) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char **mtdbuf);
/* We probably shouldn't allow XIP if the unpoint isn't a NULL */
void (*unpoint) (struct mtd_info *mtd, u_char * addr, 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);
int (*read_ecc) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf, u_char *eccbuf, struct nand_oobinfo *oobsel);
int (*write_ecc) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf, u_char *eccbuf, struct nand_oobinfo *oobsel);
int (*read_oob) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
int (*write_oob) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);
/*
* 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 (*read_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
int (*read_fact_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
/* This function is not yet implemented */
int (*write_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
#if 0
/* kvec-based read/write methods. We need these especially for NAND flash,
with its limited number of write cycles per erase.
NB: The 'count' parameter is the number of _vectors_, each of
which contains an (ofs, len) tuple.
*/
int (*readv) (struct mtd_info *mtd, struct kvec *vecs, unsigned long count, loff_t from, size_t *retlen);
int (*readv_ecc) (struct mtd_info *mtd, struct kvec *vecs, unsigned long count, loff_t from,
size_t *retlen, u_char *eccbuf, struct nand_oobinfo *oobsel);
int (*writev) (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen);
int (*writev_ecc) (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to,
size_t *retlen, u_char *eccbuf, struct nand_oobinfo *oobsel);
#endif
/* Sync */
void (*sync) (struct mtd_info *mtd);
#if 0
/* Chip-supported device locking */
int (*lock) (struct mtd_info *mtd, loff_t ofs, size_t len);
int (*unlock) (struct mtd_info *mtd, loff_t ofs, size_t len);
/* Power Management functions */
int (*suspend) (struct mtd_info *mtd);
void (*resume) (struct mtd_info *mtd);
#endif
/* Bad block management functions */
int (*block_isbad) (struct mtd_info *mtd, loff_t ofs);
int (*block_markbad) (struct mtd_info *mtd, loff_t ofs);
void *priv;/*私有数据, cfi接口flash指向map_info结构, 或指向自定义flash相关结构体(nand_chip)*/
struct module *owner;
int usecount;
};
nand_chip()的结构体类型为nand_chip
提供控制Nandflashd的控制接口,用户可根不同的Nand Controller写自己的控制函数,再赋值给该结构体的指针变量,也可使用系统默认的值作为函数
struct nand_chip {
void __iomem *IO_ADDR_R;/*8 位NAND 芯片的读地址,PIO模式下NAND_flash使用MTD需要设置的地址*/
void __iomem *IO_ADDR_W;/*8 位NAND 芯片的读写地址,PIO模式下NAND_flash使用MTD需要设置的地址
*/
u_char (*read_byte)(struct mtd_info *mtd);
void (*write_byte)(struct mtd_info *mtd, u_char byte);
u16 (*read_word)(struct mtd_info *mtd);
void (*write_word)(struct mtd_info *mtd, u16 word);
void (*write_buf)(struct mtd_info *mtd, const u_char *buf, int len);
void (*read_buf)(struct mtd_info *mtd, u_char *buf, int len);
int (*verify_buf)(struct mtd_info *mtd, const u_char *buf, int len);
void (*select_chip)(struct mtd_info *mtd, int chip);/*选择一块NAND flash 芯片*/
int (*block_bad)(struct mtd_info *mtd, loff_t ofs, int getchip);/*读出坏块*/
int (*block_markbad)(struct mtd_info *mtd, loff_t ofs);
void (*hwcontrol)(struct mtd_info *mtd, int cmd);//发送控制指令
int (*dev_ready)(struct mtd_info *mtd);//判断Nandflash是否繁忙
void (*cmdfunc)(struct mtd_info *mtd, unsigned command, int column, int page_addr);/*向NAND 芯片发起命令*/
int (*waitfunc)(struct mtd_info *mtd, struct nand_chip *this, int state);/*等待命令完成*/
int (*calculate_ecc)(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code);
int (*correct_data)(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc);
void (*enable_hwecc)(struct mtd_info *mtd, int mode);
void (*erase_cmd)(struct mtd_info *mtd, int page);
int (*scan_bbt)(struct mtd_info *mtd);
int eccmode;//ECC校验模式-软件-硬件
int eccsize;
int eccbytes;
int eccsteps;
int chip_delay;//芯片时序延迟参数
#if 0
spinlock_t chip_lock;
wait_queue_head_t wq;
nand_state_t state;
#endif
int page_shift;
int phys_erase_shift;
int bbt_erase_shift;
int chip_shift;
u_char *data_buf;
u_char *oob_buf;
int oobdirty;
u_char *data_poi;
unsigned int options;
int badblockpos;
int numchips;
unsigned long chipsize;
int pagemask;
int pagebuf;
struct nand_oobinfo *autooob;
uint8_t *bbt;
struct nand_bbt_descr *bbt_td;
struct nand_bbt_descr *bbt_md;
struct nand_bbt_descr *badblock_pattern;
struct nand_hw_control *controller;
void *priv;
};
nand_init_chip(&nand_info[i], &nand_chip[i], base_address[i]);
nand_init_chip()的本质是初始化这些结构体变量,MTD内存技术驱动程序,MTD技术的目的是使memory的使用在系统中更加方便
static void nand_init_chip(struct mtd_info *mtd, struct nand_chip *nand,
ulong base_addr)
{
mtd->priv = nand;
nand->IO_ADDR_R = nand->IO_ADDR_W = (void __iomem *)base_addr;
board_nand_init(nand);/*初始化nand_chip 结构体,设置Nand_flash 读写时序寄存器和ECC
chip->IO_ADDR_R = (void *)&s3c2440nand->NFDATA;//获取读硬件地址
chip->IO_ADDR_W = (void *)&s3c2440nand->NFDATA;//获取写硬件地址
chip->hwcontrol = s3c2440_nand_hwcontrol;//命令控制函数
chip->dev_ready = s3c2440_nand_devready;//获取NANDflash是否繁忙函数
chip->select_chip = s3c2440_nand_select_chip;//片选信号控制函数
chip->options = 0;
Set flash memory timing
s3c2440nand->NFCONF = (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4);
Initialize ECC, enable chip select, NAND flash controller enable
s3c2440nand->NFCONT = (1<<4)|(0<<1)|(1<<0);*/
if (nand_scan(mtd, 1) == 0) {
if (!mtd->name)
mtd->name = (char *)default_nand_name;
} else
mtd->name = NULL;
}
nand_scan(mtd, 1)的作用首先是初始化没有初始化过的nand_chip结构体(注册一系列函数),获取NANDflash的基本信息,并扫描发出片选信号,发出读ID信号,将nand_flash_ids[]数组里面原有的信息来和读到的Nandflash的ID做比较,寻找到对应ID的内容,再继续初始化Mtd。下面介绍初始化 Mtd的主要过程:
mtd->name = nand_flash_ids[i].name;//初始化名称
mtd->oobblock = 1024 << (extid & 0x3);//设置oob块大小
mtd->oobsize = (8 << (extid & 0x03)) * (mtd->oobblock / 512);//设置oob大小
mtd->erasesize = (64 * 1024) << (extid & 0x03);//设置擦除大小
通过上述的函数,初始化了MTD功能,接着回到主程序
void env_relocate (void)
{
DEBUGF ("%s[%d] offset = 0x%lx\n", __FUNCTION__,__LINE__,
gd->reloc_off);
#ifdef ENV_IS_EMBEDDED//0
/*
* The environment buffer is embedded with the text segment,
* just relocate the environment pointer
*/
env_ptr = (env_t *)((ulong)env_ptr + gd->reloc_off);//env_ptr 重定位
DEBUGF ("%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 (CFG_ENV_SIZE);/*在ENV_IS_EMBEDDED未定义的情况下为环境变量分配空间,实际上ENV_IS_EMBEDDED为未定义*/
DEBUGF ("%s[%d] malloced ENV at %p\n", __FUNCTION__,__LINE__,env_ptr);
#endif
/*
* After relocation to RAM, we can always use the "memory" functions
*/
env_get_char = env_get_char_memory;//定义env_get_char函数
if (gd->env_valid == 0) {//如果前面的env_init中出现校验错误则这里打印错误信息
#if defined(CONFIG_GTH) || defined(CFG_ENV_IS_NOWHERE) /* Environment not changable */
puts ("Using default environment\n\n");
#else
puts ("*** Warning - bad CRC, using default environment\n\n");
SHOW_BOOT_PROGRESS (-1);
#endif
if (sizeof(default_environment) > ENV_SIZE)
{
puts ("*** Error - default environment is too large\n\n");
return;
}
//若CRC出现错误,则从新初始化环境变量
memset (env_ptr, 0, sizeof(env_t));
memcpy (env_ptr->data,
default_environment,
sizeof(default_environment));
#ifdef CFG_REDUNDAND_ENVIRONMENT
env_ptr->flags = 0xFF;
#endif
env_crc_update ();
gd->env_valid = 1;
}
else {//若之前环境变量成功的初始化了
env_relocate_spec ();//实际上为空函数
}
gd->env_addr = (ulong)&(env_ptr->data);
#ifdef CONFIG_AMIGAONEG3SE
disable_nvram();
#endif
}
env_relocate ();初始化环境变量,之前env_init()将gd->env_addr 指向了default_environment,env_relocate()函数从新给环境变量分配内存,将原有的环境变量复制到新空间,并将地址指向新空间。
接下来主函数在环境变量中查找IP和MAC地址,赋值给gd结构体。
devices_init ();该函数的目的是创建驱动列表项
int devices_init (void)
{
#ifndef CONFIG_ARM /* already relocated for current ARM implementation */
ulong relocation_offset = gd->reloc_off;
int i;
for (i = 0; i < (sizeof (stdio_names) / sizeof (char *)); ++i) {
stdio_names[i] = (char *) (((ulong) stdio_names[i]) +
relocation_offset);
}
#endif
devlist = ListCreate (sizeof (device_t));//创造一个驱动列表项节点
serial_devices_init ();
return (0);
}
ListCreate ()函数的作用为创造和初始化一个列表节点,
list_t ListCreate (int elementSize)
{
list_t list;
list = (list_t) (NewHandle (sizeof (ListStruct))); create empty list
if (list) {
(*list)->signature = LIST_SIGNATURE;
(*list)->numItems = 0;
(*list)->listSize = 0;
(*list)->itemSize = elementSize;
(*list)->percentIncrease = kDefaultAllocationPercentIncrease;
(*list)->minNumItemsIncrease =
kDefaultAllocationminNumItemsIncrease;
}
return list;
}*/
其中NewHandle用来分配列表空间
Handle NewHandle (unsigned int numBytes)
{
void *memPtr;
HandleRecord *hanPtr;
memPtr = calloc (numBytes, 1);
hanPtr = (HandleRecord *) calloc (sizeof (HandleRecord), 1);
if (hanPtr && (memPtr || numBytes == 0)) {
hanPtr->ptr = memPtr;
hanPtr->size = numBytes;
return (Handle) hanPtr;
} else {
free (memPtr);
free (hanPtr);
return NULL;
}
}
下面一串口驱动加载到列表为例子说明:
首先定义一个串口类型的驱动,在将该驱动挂载到列表中,重要的函数是分析device_register
void serial_devices_init (void)
{
device_t 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 = s->init;
dev.putc = s->putc;
dev.puts = s->puts;
dev.getc = s->getc;
dev.tstc = s->tstc;
device_register (&dev);
s = s->next;
}
}
device_register()分析
//传入的参数分别是 列表、节点 、位置、
int ListInsertItems (list_t list, void *ptrToItems, int firstItemPosition,
int numItemsToInsert)
{
int numItems = (*list)->numItems;
if (firstItemPosition == numItems + 1)//判断插入的位置,只能连续插入
firstItemPosition = LIST_END;
else if (firstItemPosition > numItems)//不连续插曲,跳出
return 0;
if ((*list)->numItems >= (*list)->listSize) {//判断插入位置是否超出
if (!ExpandListSpace (list, -numItemsToInsert))
return 0;
}
if (firstItemPosition == LIST_START) {
if (numItems == 0) {
/* special case for empty list */
firstItemPosition = LIST_END;
} else {
firstItemPosition = 1;
}
}
if (firstItemPosition == LIST_END) { /* add at the end of the list */
if (ptrToItems)
memcpy (ITEMPTR (list, numItems), ptrToItems,
(*list)->itemSize * numItemsToInsert);
else
memset (ITEMPTR (list, numItems), 0,
(*list)->itemSize * numItemsToInsert);
(*list)->numItems += numItemsToInsert;
} else { /* move part of list up to make room for new item */
memmove (ITEMPTR (list, firstItemPosition - 1 + numItemsToInsert),
ITEMPTR (list, firstItemPosition - 1),
(numItems + 1 - firstItemPosition) * (*list)->itemSize);
if (ptrToItems)
memmove (ITEMPTR (list, firstItemPosition - 1), ptrToItems,
(*list)->itemSize * numItemsToInsert);
else
memset (ITEMPTR (list, firstItemPosition - 1), 0,
(*list)->itemSize * numItemsToInsert);
(*list)->numItems += numItemsToInsert;
}
return 1;
}