Bootloader 它的终极使命就是启动内核,它的主要工作:在上电之后在硬件上进行一系列的初始化,为Linux内核启动铺路。具体的就是设置看门狗、设置系统时钟、初始化SDRAM与NANDflash控制器、代码重定位等,最后将内核从nandflash上拷贝到内存中,传递参数,跳转执行。
一、创建start.S文件
.text
.global _start
_start:
/* close the watchdog */
ldr r0, =0x53000000
mov r1, #0
str r1, [r0]
/* close the watchdog */
@关闭看门狗
@数据手册:WTCON 0x53000000 R/W Watchdog timer control register
/* set the clock */
ldr r0, =0x4c000014
mov r1, #0x03; @ FCLK:HCLK:PCLK=1:2:4, HDIVN=1,PDIVN=1
str r1, [r0]
mrc p15, 0, r1, c1, c0, 0 /* read */
orr r1, r1, #0xc0000000 /* set asynchronous bus mode */
mcr p15, 0, r1, c1, c0, 0 /* write */
ldr r0, =0x4c000004
ldr r1, =((0x5c<<12)|(0x01<<4)|(0x02)) @MPLL:200MHz
str r1, [r0]
/* set the clock */
@设置系统时钟
@如果HDIVN不是0,CPU的总线模式应该设置成asynchronous bus mode
/* enable the ICACHE */
mrc p15, 0, r0, c1, c0, 0 @ read control register
orr r0, r0, #(1<<12)
mcr p15, 0, r0, c1, c0, 0 @ write back
/* enable the ICACHE */
@使能高速缓存,为系统提速,此段可不要,但程序执行速度要慢
/* init the SDRAM */
ldr r0, =0x48000000 @MEM_CTL_BASE
adr r1, config /* sdram config address */
add r3, r0, #(52) @13*4
1:
ldr r2, [r1], #4
str r2, [r0], #4
cmp r0, r3
bne 1b
/* init the SDRAM */
@初始化SDRAM
@根据数据手册对与SDRAM有关的13个寄存器进行配置
/* relocate */
ldr sp, =0x34000000
bl nand_init
mov r0, #0
ldr r1, =_start
ldr r2, =__bss_start
sub r2, r2, r1
bl copy_code_to_sdram
bl clear_bss
/* relocate */
@把bootloader本身的代码从nandflash复制到它的链接地址去
/* go to main */
ldr lr, =halt
ldr pc, =main
halt:
b halt
/* go to main */
@跳转到main函数执行
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文件
/* nandflash controller */
#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);
void copy_code_to_sdram(unsigned char *src, unsigned char *dest, unsigned int len)
{
nand_read((unsigned int)src, dest, len);
}
void clear_bss(void)
{
extern int __bss_start, __bss_end;
int *p = &__bss_start;
for (; p < &__bss_end; p++) *p = 0;
}
void nand_init(void)
{
#define TACLS 0
#define TWRPH0 1
#define TWRPH1 0
/* sequence */
NFCONF = (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4);
/* enable controller,init ECC,disable CS */
NFCONT = (1<<4)|(1<<1)|(1<<0);
}
void nand_select(void)
{
NFCONT &= ~(1<<1);
}
void nand_deselect(void)
{
NFCONT |= (1<<1);
}
void nand_cmd(unsigned char cmd)
{
volatile int i;
NFCMMD = cmd;
for (i = 0; i < 10; i++);
}
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++);
}
void nand_wait_ready(void)
{
while (!(NFSTAT & 1));
}
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;
/* select */
nand_select();
while (i < len)
{
/* read cmd 00h */
nand_cmd(0x00);
/* send addr */
nand_addr(addr);
/* read cmd 30h */
nand_cmd(0x30);
/* check */
nand_wait_ready();
/* read data */
for (; (col < 2048) && (i < len); col++)
{
buf[i] = nand_data();
i++;
addr++;
}
col = 0;
}
/* deselect */
nand_deselect();
}
#define PCLK 50000000 // PCLK:50MHz
#define UART_CLK PCLK // UART0:PCLK
#define UART_BAUD_RATE 115200 // baudrate
#define UART_BRD ((UART_CLK / (UART_BAUD_RATE * 16))-1)
/*115200,8N1,none*/
void uart0_init(void)
{
GPHCON |= 0xa0; // GPH2,GPH3:TXD0,RXD0
GPHUP = 0x0c; // GPH2,GPH3 pull up
ULCON0 = 0x03;
UCON0 = 0x05;
UFCON0 = 0x00;
UMCON0 = 0x00;
UBRDIV0 = UART_BRD; // 115200
}
void putc(unsigned char c)
{
while (!(UTRSTAT0 & TXD0READY));
UTXH0 = c;
}
void puts(char *str)
{
int i = 0;
while (str[i])
{
putc(str[i]);
i++;
}
}
完成了 nandflash与串口的初始化。
三、创建boot.c文件
#include "setup.h"
extern void uart0_init(void);
extern void nand_read(unsigned int addr, unsigned char *buf, unsigned int len);
extern void puts(char *str);
extern void puthex(unsigned int val);
static struct tag *params;
void setup_start_tag(void)
{
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);
}
void setup_memory_tags(void)
{
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);
}
int strlen(char *str)
{
int i = 0;
while (str[i])
{
i++;
}
return i;
}
void strcpy(char *dest, char *src)
{
while ((*dest++ = *src++) != '