016_ JZ2440之u_boot编写

跟着韦东山老师学习JZ2440 u_boot的编写。

今天我们自己写一个简易的u_boot来启动kernel。

u_boot的启动流程我们默认分为两个阶段:

第一阶段   硬件驱动配置。

第二阶段  u_boot启动参数设置。

接下来我们先分析一下:

硬件驱动配置:

硬件启动流程

1、关看门狗。

2、设置CPU的工作时钟频率。(目前设置时钟工作频率是200MHz)

3、初始化SDRAM。

4、重定向代码。(当代码段大于4K的时候,需要把代码段从nand flash拷贝到sdram中运行)

这些工作主要在start.S中完成。关于nand flash的内容我们放到init.c中执行。

接下来上代码:

start.S

/**
  ******************************************************************************
  * @file      : start.S 
  * @version   : none
  * @date      : 2019-12-16
  * @brief     : init JZ2440 Board peripheral
  ******************************************************************************
  * @attention : JZ2440 EVAL BOARD
  * @author    : shao
  * @personal  : shao work stdio
  ******************************************************************************
  */ 

/**
 * 注:init.S主要用来初始化第一阶段的硬件。
 *     全部由汇编编写。
 */
#define S3C2440_MPLL_200MHZ     ((0x5c<<12)|(0x01<<4)|(0x02))
#define MEM_CTL_BASE    		0x48000000

.text
.global _start
_start:


/* 1. 关看门狗 */
	ldr r0, =0x53000000
	mov r1, #0x00
	str r1, [r0]

/* 2. 设置时钟 */
	ldr r0, =0x4C000014
	mov r1, #0x03  // FCLK:HCLK:PCLK = 1:2:4
	str r1, [r0]  

	/* 当FLCK != HCLK时, CPU必须由Fast mode改为 Asynchronous mode */
	mrc	p15, 0, r1, c1, c0, 0		/* 读出控制寄存器 */ 
	orr	r1, r1, #0xc0000000			/* 设置为“asynchronous bus mode” */
	mcr	p15, 0, r1, c1, c0, 0		/* 写入控制寄存器 */

	/* MPLLCON = S3C2440_MPLL_200MHZ */
	ldr r0, =0x4C000004
	ldr r1, =S3C2440_MPLL_200MHZ
	str r1, [r0]

/* 3. 初始化SDRAM(运行内存,代码重定位用) */
	ldr r0, =MEM_CTL_BASE
	adr r1, sdram_config /* sdram_config函数的当前地址 */
	add r3, r0, #(13*4)   /* 求和: r3 = r0+13*4 */
	/* 循环向 MEM_CTL_BASE到MEM_CTL_BASE + 13*4的寄存器写入数据*/
1:
	ldr r2, [r1], #4  /* r2=r1, r1地址加4 */
	str r2, [r0], #4  /* r0=r2, r0地址加4 */
	cmp r0, r3  /* 比较r0, r3是否相等 */
	bne 1b  /* 若果r0, r3 不相等,那么继续回到1b */

/* 4. 重定位 */
	ldr sp, =0x34000000

	bl nand_init

	mov r0, #0				//传参的起始地址
	ldr r1, =_start			//srcAddr代码起始地址
	ldr r2, =__bss_start	//代码结束地址
	sub r2, r2, r1  		//代码长度,r2=r2-r1 
	/* 汇编调用C的时候,可以用r0~r3来传参数 
	 * param1:r0
	 * param2: r1
	 * param3: r2
	 */
	bl copy_code_to_sdram
	bl clear_bss

/* 5. 执行main */
	ldr lr, =halt
	ldr pc, =main
halt:
	b halt


sdram_config:
	.long 0x22011110	 //BWSCON
	.long 0x00000700	 //BANKCON0
	.long 0x00000700	 //BANKCON1
	.long 0x00000700	 //BANKCON2
	.long 0x00000700	 //BANKCON3  
	.long 0x00000700	 //BANKCON4
	.long 0x00000700	 //BANKCON5
	.long 0x00018005	 //BANKCON6
	.long 0x00018005	 //BANKCON7
	.long 0x008C04F4	 //REFRESH
	.long 0x000000B1	 //BANKSIZE
	.long 0x00000030	 //MRSRB6
	.long 0x00000030	 //MRSRB7


然后是init.c。在这里我们需要init一下uart,方便调试和信息输出。

/**
  ******************************************************************************
  * @file      : init.c 
  * @version   : none
  * @date      : 2019-12-16
  * @brief     : init JZ2440 Board peripheral
  ******************************************************************************
  * @attention : JZ2440 EVAL BOARD
  * @author    : shao
  * @personal  : shao work stdio
  ******************************************************************************
  */ 

/* NAND FLASH控制器 */
#define NFCONF (*((volatile unsigned long *)0x4E000000))
#define NFCONT (*((volatile unsigned long *)0x4E000004))
#define NFCMMD (*((volatile unsigned char *)0x4E000008))
#define NFADDR (*((volatile unsigned char *)0x4E00000C))
#define NFDATA (*((volatile unsigned char *)0x4E000010))
#define NFSTAT (*((volatile unsigned char *)0x4E000020))

//==========================串口设置==========================================
/* GPIO */
#define GPHCON              (*(volatile unsigned long *)0x56000070)
#define GPHUP               (*(volatile unsigned long *)0x56000078)

/* UART registers*/
#define ULCON0              (*(volatile unsigned long *)0x50000000)
#define UCON0               (*(volatile unsigned long *)0x50000004)
#define UFCON0              (*(volatile unsigned long *)0x50000008)
#define UMCON0              (*(volatile unsigned long *)0x5000000c)
#define UTRSTAT0            (*(volatile unsigned long *)0x50000010)
#define UTXH0               (*(volatile unsigned char *)0x50000020)
#define URXH0               (*(volatile unsigned char *)0x50000024)
#define UBRDIV0             (*(volatile unsigned long *)0x50000028)

#define TXD0READY   (1<<2)


void nand_read(unsigned int addr, unsigned char *buf, unsigned int len);


/**
 * brief:write data to address 0, then read 
 *       back this address data.
 *       if data equal to write data, then 
 *       address 0 can write and read. So boot 
 *       from nand falsh.
 *       else boot from nor flash.
 */
int isBootFromNorFlash(void)
{
	volatile int *p = (volatile int *)0; //让p指向0地址
	int val;

	val = *p;  //先保存当前0地址的数据
	*p = 0x12345678; //向0地址写入数据
	if(*p == 0x12345678)
	{
		/* 写成功,nand 启动       */
		*p = val;  //把值还原回去
		return 0;
	}
	else
	{
		/* Nor Flash 启动 */
		return 1;
	}
}



/**
 * brief: copy code from flash to SDRAM
 * param: *src code start source address
 * param: *dest code destination address
 * param: code length
 */
void copy_code_to_sdram(unsigned char *src, unsigned char *dest, unsigned int len)
{
	int i = 0;

	/* 如果是Nor Flash   启动 */
	if(isBootFromNorFlash())
	{
		while(i < len)
		{
			dest[i] = src[i];
			i++;
		}
	}
	else
	{
		//nand_init();  		//放到start.S中去做了
		nand_read((unsigned int)src, dest, len);
	}
}

/**
 * brief: clear bss
 */
void clear_bss(void)
{
	extern int __bss_start, __bss_end;  //获取lds文件中变量的固定格式
	int *p = &__bss_start; //让p指向__bss_start地址

	for(;p < &__bss_end; p++)
	{
		*p = 0;
	}
}

/**
 * brief: init nand flash
 */
void nand_init(void)
{
#define TACLS   0
#define TWRPH0  1
#define TWRPH1  0

	/* 设置时序 */
	NFCONF = (TACLS<<12) | (TWRPH0<<8) | (TWRPH1<<4);
	/* 使能 nand flash控制器, 初始化ECC, 禁止片选 */
	NFCONT = (1<<4) | (1<<1) | (1<<0);
}

/**
 * brief: select nand flash
 */
void nand_select(void)
{
	NFCONT &= ~(1<<1);  //拉低选中
}

/**
 * brief: deselect nand flash
 */

void nand_deselect(void)
{
	NFCONT |= (1<<1);//拉高释放
}

/**
 * brief: send nand flash control command
 * param: cmd command type
 */
void nand_cmd(unsigned char cmd)
{
	volatile int i;
	NFCMMD = cmd;
	for(i= 0; i < 10; i++);  //延时一小会,经验之作
}

/**
 * brief: send r/w address
 * param: addr 
 */
void nand_addr(unsigned int addr)
{
	unsigned int col = addr % 2048;
	unsigned int page = addr / 2048;
	volatile int i;

	NFADDR = col & 0xff;
	for(i = 0; i < 10; i++); //延时一小会
	NFADDR = (col >> 8) & 0xff;
	for(i = 0; i < 10; i++); //延时一小会

	NFADDR = page & 0xff;
	for(i = 0; i < 10; i++); //延时一小会
	NFADDR = (page >> 8) & 0xff;
	for(i = 0; i < 10; i++); //延时一小会
	NFADDR = (page >> 16) & 0xff;
	for(i = 0; i < 10; i++); //延时一小会
}

/**
 * brief: wait for nand flash ready
 */
void nand_wait_ready(void)
{
	while(!(NFSTAT & 1));
}

/**
 * brief: return current data
 */
unsigned char nand_data(void)
{
	return NFDATA;
}

void nand_read(unsigned int addr, unsigned char *buf, unsigned int len)
{
	int col = addr % 2048;
	int i = 0;

	/* 1. 选中 */
	nand_select();
	
	while(i < len)
	{
		/* 2. 发出读命令 00h*/
		nand_cmd(0x00);

		/* 3. 发出地址(分五步发出) */
		nand_addr(addr);

		/* 4. 发出读命令30h */
		nand_cmd(0x30);

		/* 5. 判断状态 */
		nand_wait_ready();

		/* 6. 读数据 */
		for(; (col < 2048) && (i < len); col++)
		{
			buf[i] = nand_data();
			i++;
			addr++;
		}

		col = 0;
	}
	
	/* 7. 取消选中 */
	nand_deselect();
}


//===========================串口部分的设置======================
#define PCLK            50000000    // init.c中的clock_init函数设置PCLK为50MHz
#define UART_CLK        PCLK        //  UART0的时钟源设为PCLK
#define UART_BAUD_RATE  115200      // 波特率
#define UART_BRD        ((UART_CLK  / (UART_BAUD_RATE * 16)) - 1)

/*
 * 初始化UART0
 * 115200,8N1,无流控
 */
void uart0_init(void)
{
    GPHCON  |= 0xa0;    // GPH2,GPH3用作TXD0,RXD0
    GPHUP   = 0x0c;     // GPH2,GPH3内部上拉

    ULCON0  = 0x03;     // 8N1(8个数据位,无较验,1个停止位)
    UCON0   = 0x05;     // 查询方式,UART时钟源为PCLK
    UFCON0  = 0x00;     // 不使用FIFO
    UMCON0  = 0x00;     // 不使用流控
    UBRDIV0 = UART_BRD; // 波特率为115200
}

/*
 * 发送一个字符
 */
void putc(unsigned char c)
{
    /* 等待,直到发送缓冲区中的数据已经全部发送出去 */
    while (!(UTRSTAT0 & TXD0READY));
    
    /* 向UTXH0寄存器中写入数据,UART即自动将它发送出去 */
    UTXH0 = c;
}

void puts(char *str)
{
	int i = 0;
	while (str[i])
	{
		putc(str[i]);
		i++;
	}
}

void puthex(unsigned int val)
{
	/* 0x1234abcd */
	int i;
	int j;
	
	puts("0x");

	for (i = 0; i < 8; i++)
	{
		j = (val >> ((7-i)*4)) & 0xf;
		if ((j >= 0) && (j <= 9))
			putc('0' + j);
		else
			putc('A' + j - 0xa);
		
	}
	
}


 还需要设置一下lds连接脚本,来设置代码存放的段区。

boot.lds

SECTIONS{
	. = 0x33f80000;
	.text : { *(.text) }

	. = ALIGN(4);
	.rodata : { *(.rodata*) }

	. = ALIGN(4);
	.data : { *(.data) }

	. = ALIGN(4);
	__bss_start = .;
	.bss : { *(.bss) *(COMMON) }
	__bss_end = .;
}

这里用的是4字节对齐。 

到这里第一阶段基本完成,硬件驱动也写完了(其他功能可以再添加这里是最简化的版本)。 

u_boot启动参数设置:

0、初始化串口

1、从nand flash 把内核读入内存

2、设置参数

3、跳转执行

boot.c

/**
  ******************************************************************************
  * @file      : boot.c 
  * @version   : none
  * @date      : 2019-12-18
  * @brief     : init JZ2440 Board peripheral
  ******************************************************************************
  * @attention : JZ2440 EVAL BOARD
  * @author    : shao
  * @personal  : shao work stdio
  ******************************************************************************
  */ 
#include "setup.h"

extern void uart0_init(void);
extern void putc(unsigned char c);
extern void puts(char *str);
extern void puthex(unsigned int val);

extern void nand_read(unsigned int addr, unsigned char *buf, unsigned int len);



static struct tag *params;

/**
 * brief: set start tag
 */

void setup_start_tag()
{
	params = (struct tag *)0x30000100;

	params->hdr.tag = ATAG_CORE;
	params->hdr.size = tag_size(tag_core);

	params->u.core.flags = 0;
	params->u.core.pagesize = 0;
	params->u.core.rootdev = 0;

	params = tag_next(params);
}

/**
 * brief: set memory tag
 */
void setup_memory_tags()
{
	params->hdr.tag = 
ATAG_MEM;
	params->hdr.size = tag_size(tag_mem32);

	params->u.mem.start = 0x30000000;
	params->u.mem.size = 64*1024*1024;

	params = tag_next(params);
}

/**
 * brief: caculate string length
 * *str : string to be caculate
 */
int strlen(char *str)
{
	int i = 0; 

	while
(str[i])
	{
		i++;
	}

	return i;
}

/**
 * brief: copy string to another
 * *dest: destinate string
 * *src : source string
 */
void strcopy(char *dest, char *src)
{
	while((*dest++ = *src++) != '\0');
}


/**
 * brief: set commandline tag
 */
void setup_commandline_tag(char *cmdline)
{
	int len = strlen(cmdline) + 1;

	params->hdr.tag = ATAG_CMDLINE;
	params->hdr.size = (sizeof(struct tag_header) + len + 3) >> 2;

	strcopy(params->u.cmdline.cmdline, cmdline);

	params = tag_next(params);
}

/**
 * brief: set end tag
 */
void setup_end_tag()
{
	params->hdr.tag = ATAG_NONE;
	params->hdr.size = 0;
}



int main(void)
{
	void (*theKernel)(int zero, int arch, unsigned int params);
	volatile unsigned int *p = (volatile unsigned int *)0x30008000;

	/* 0. 设置串口,打印初始化信息*/
	uart0_init();
	puts("shao .... Config bootloader ...\r\n");
	
	/* 1. 从Nand Flash把内核读入内存 */
	puts("Copy kernel from nand!\r\n");

	/**
	 * 0x60000+64  : 内核起始地址+64byte
	 * 写入到SDRAM : 0x30008000地址
 	 * 写入长度		: 0x200000	2MB
	 */
	nand_read(0x60000+64, (unsigned char *)0x30008000, 0x200000); 
	puthex(0x1234ABCD);
	puts("\r\n");
	puthex(*p);
	puts("\r\n");


	/* 2. 设置参数 */
	puts("Set boot params.\r\n");
	setup_start_tag();
	setup_memory_tags();
	setup_commandline_tag("noinitrd root=/dev/mtdblock3 init=/linuxrc console=ttySAC0");
	setup_end_tag();
	

	/* 3. 跳转执行 */
	puts("Boot kernel... \r\n");
	theKernel = (void (*)(int, int, unsigned int))0x30008000;
	theKernel(0, 362, 0x30000100);
	

	puts("Error!\r\n");		   			//执行到这里会打印错误信息,程序正常的情况下不会执行到这里

	return -1;  				//执行到这里肯定就有错误了
}

我们借用了u_boot源码的,setup.h

/*
 *  linux/include/asm/setup.h
 *
 *  Copyright (C) 1997-1999 Russell King
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 *  Structure passed to kernel to tell it about the
 *  hardware it's running on.  See linux/Documentation/arm/Setup
 *  for more info.
 *
 * NOTE:
 *  This file contains two ways to pass information from the boot
 *  loader to the kernel. The old struct param_struct is deprecated,
 *  but it will be kept in the kernel for 5 years from now
 *  (2001). This will allow boot loaders to convert to the new struct
 *  tag way.
 */
#ifndef __ASMARM_SETUP_H
#define __ASMARM_SETUP_H

#define u8  unsigned char
#define u16 unsigned short
#define u32 unsigned long

/*
 * Usage:
 *  - do not go blindly adding fields, add them at the end
 *  - when adding fields, don't rely on the address until
 *    a patch from me has been released
 *  - unused fields should be zero (for future expansion)
 *  - this structure is relatively short-lived - only
 *    guaranteed to contain useful data in setup_arch()
 */
#define COMMAND_LINE_SIZE 1024

/* This is the old deprecated way to pass parameters to the kernel */
struct param_struct {
    union {
	struct {
	    unsigned long page_size;		/*  0 */
	    unsigned long nr_pages;		/*  4 */
	    unsigned long ramdisk_size;		/*  8 */
	    unsigned long flags;		/* 12 */
#define FLAG_READONLY	1
#define FLAG_RDLOAD	4
#define FLAG_RDPROMPT	8
	    unsigned long rootdev;		/* 16 */
	    unsigned long video_num_cols;	/* 20 */
	    unsigned long video_num_rows;	/* 24 */
	    unsigned long video_x;		/* 28 */
	    unsigned long video_y;		/* 32 */
	    unsigned long memc_control_reg;	/* 36 */
	    unsigned char sounddefault;		/* 40 */
	    unsigned char adfsdrives;		/* 41 */
	    unsigned char bytes_per_char_h;	/* 42 */
	    unsigned char bytes_per_char_v;	/* 43 */
	    unsigned long pages_in_bank[4];	/* 44 */
	    unsigned long pages_in_vram;	/* 60 */
	    unsigned long initrd_start;		/* 64 */
	    unsigned long initrd_size;		/* 68 */
	    unsigned long rd_start;		/* 72 */
	    unsigned long system_rev;		/* 76 */
	    unsigned long system_serial_low;	/* 80 */
	    unsigned long system_serial_high;	/* 84 */
	    unsigned long mem_fclk_21285;       /* 88 */
	} s;
	char unused[256];
    } u1;
    union {
	char paths[8][128];
	struct {
	    unsigned long magic;
	    char n[1024 - sizeof(unsigned long)];
	} s;
    } u2;
    char commandline[COMMAND_LINE_SIZE];
};


/*
 * The new way of passing information: a list of tagged entries
 */

/* The list ends with an ATAG_NONE node. */
#define ATAG_NONE	0x00000000

struct tag_header {
	u32 size;
	u32 tag;
};

/* The list must start with an ATAG_CORE node */
#define ATAG_CORE	0x54410001

struct tag_core {
	u32 flags;		/* bit 0 = read-only */
	u32 pagesize;
	u32 rootdev;
};

/* it is allowed to have multiple ATAG_MEM nodes */
#define ATAG_MEM	0x54410002

struct tag_mem32 {
	u32	size;
	u32	start;	/* physical start address */
};

/* VGA text type displays */
#define ATAG_VIDEOTEXT	0x54410003

struct tag_videotext {
	u8		x;
	u8		y;
	u16		video_page;
	u8		video_mode;
	u8		video_cols;
	u16		video_ega_bx;
	u8		video_lines;
	u8		video_isvga;
	u16		video_points;
};

/* describes how the ramdisk will be used in kernel */
#define ATAG_RAMDISK	0x54410004

struct tag_ramdisk {
	u32 flags;	/* bit 0 = load, bit 1 = prompt */
	u32 size;	/* decompressed ramdisk size in _kilo_ bytes */
	u32 start;	/* starting block of floppy-based RAM disk image */
};

/* describes where the compressed ramdisk image lives (virtual address) */
/*
 * this one accidentally used virtual addresses - as such,
 * its depreciated.
 */
#define ATAG_INITRD	0x54410005

/* describes where the compressed ramdisk image lives (physical address) */
#define ATAG_INITRD2	0x54420005

struct tag_initrd {
	u32 start;	/* physical start address */
	u32 size;	/* size of compressed ramdisk image in bytes */
};

/* board serial number. "64 bits should be enough for everybody" */
#define ATAG_SERIAL	0x54410006

struct tag_serialnr {
	u32 low;
	u32 high;
};

/* board revision */
#define ATAG_REVISION	0x54410007

struct tag_revision {
	u32 rev;
};

/* initial values for vesafb-type framebuffers. see struct screen_info
 * in include/linux/tty.h
 */
#define ATAG_VIDEOLFB	0x54410008

struct tag_videolfb {
	u16		lfb_width;
	u16		lfb_height;
	u16		lfb_depth;
	u16		lfb_linelength;
	u32		lfb_base;
	u32		lfb_size;
	u8		red_size;
	u8		red_pos;
	u8		green_size;
	u8		green_pos;
	u8		blue_size;
	u8		blue_pos;
	u8		rsvd_size;
	u8		rsvd_pos;
};

/* command line: \0 terminated string */
#define ATAG_CMDLINE	0x54410009

struct tag_cmdline {
	char	cmdline[1];	/* this is the minimum size */
};

/* acorn RiscPC specific information */
#define ATAG_ACORN	0x41000101

struct tag_acorn {
	u32 memc_control_reg;
	u32 vram_pages;
	u8 sounddefault;
	u8 adfsdrives;
};

/* footbridge memory clock, see arch/arm/mach-footbridge/arch.c */
#define ATAG_MEMCLK	0x41000402

struct tag_memclk {
	u32 fmemclk;
};

struct tag {
	struct tag_header hdr;
	union {
		struct tag_core		core;
		struct tag_mem32	mem;
		struct tag_videotext	videotext;
		struct tag_ramdisk	ramdisk;
		struct tag_initrd	initrd;
		struct tag_serialnr	serialnr;
		struct tag_revision	revision;
		struct tag_videolfb	videolfb;
		struct tag_cmdline	cmdline;

		/*
		 * Acorn specific
		 */
		struct tag_acorn	acorn;

		/*
		 * DC21285 specific
		 */
		struct tag_memclk	memclk;
	} u;
};

struct tagtable {
	u32 tag;
	int (*parse)(const struct tag *);
};


#define tag_member_present(tag,member)				\
	((unsigned long)(&((struct tag *)0L)->member + 1)	\
		<= (tag)->hdr.size * 4)

#define tag_next(t)	((struct tag *)((u32 *)(t) + (t)->hdr.size))
#define tag_size(type)	((sizeof(struct tag_header) + sizeof(struct type)) >> 2)

#define for_each_tag(t,base)		\
	for (t = base; t->hdr.size; t = tag_next(t))

/*
 * Memory map description
 */
#define NR_BANKS 8

struct meminfo {
	int nr_banks;
	unsigned long end;
	struct {
		unsigned long start;
		unsigned long size;
		int           node;
	} bank[NR_BANKS];
};

extern struct meminfo meminfo;

#endif

 最后执行一下Makefile

CC       = arm-linux-gcc
LD       = arm-linux-ld
AR       = arm-linux-ar
OBJCOPY  = arm-linux-objcopy
OBJDUMP  = arm-linux-objdump

CFLAGS     := -Wall -O2
CPPFLAGS   := -nostdinc -nostdlib -fno-builtin

objs := start.o init.o boot.o

boot.bin : $(objs)
	${LD} -Tboot.lds -o boot.elf $^
	${OBJCOPY} -O binary -S boot.elf $@
	${OBJDUMP} -D -m arm boot.elf > boot.dis

%.o : %.c
	${CC} $(CPPFLAGS) $(CFLAGS) -c -o $@ $<

%.o : %.S
	${CC} $(CPPFLAGS) $(CFLAGS) -c -o $@ $<

clean:
	rm -f *.o *.bin *.elf *.dis

放到Ubuntu16.04中进行编译。

得到boot.bin文件烧录到JZ2440中可以看到打印的初始化信息:

红色的部分是我们u_boot的初始化信息。

到这里我们的u_boot基本功能,启动内核的任务就完成了。 

发布了82 篇原创文章 · 获赞 2 · 访问量 3996

猜你喜欢

转载自blog.csdn.net/shao15232/article/details/103769747
今日推荐