基于S3C 2440 的 U-BOOT 启动代码之 start_armboot() 源代码分析

基于S3C 2440 的 U-BOOT 启动代码之 start_armboot() 源代码分析

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代表的是堆区域。

基于S3C 2440 的 U-BOOT 启动代码之 start_armboot() 源代码分析_第1张图片

__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
基于S3C 2440 的 U-BOOT 启动代码之 start_armboot() 源代码分析_第2张图片在这里插入图片描述

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;
}

你可能感兴趣的:(uboot,嵌入式,操作系统)