bootloader功能介绍
初始化开发板上主要硬件(时钟,内存,硬盘),
把操作系统从硬盘拷贝到内存,然后让cpu跳转到内存中执行操作系统。
boot阶段
1.关闭影响CPU正常执行的外设
-关闭看门狗(watch dog)
WTCON 0xE2700000
-关闭中断
CPSR I和F位设置为1,关闭,不响应任何中断。
2.初始化时钟
-倍频到1Ghz,为外设分频
*串口驱动
3.初始化内存控制器,DDRAM
-验证内存,往里面写一个值,然后再读出来
4.初始化硬盘,nand Flash
-nand flash 读驱动(从nand往外读数据)
loader阶段
1.从硬盘指定的地址加载kernel到内存指定的地址。
2.跳转到内存kernel所在的地址,执行
附加功能:
实现bootloader中shell(命令解释器)
附加功能:
实现bootloader中shell(命令解释器)
uboot中支持的命令,
help
loadb 下载程序, kermit 协议
go 0X21000000;
例如:在uboot中直接控制蜂鸣器
mm 0xe02000a0 0x1(控制寄存器)
mm 0xe02000a4 0x1(数据寄存器)
常用调试手段:
1.led点灯大法
2.串口调试,uart_getchar,uart_putchar,进一步实现stdio.h
时钟初始化设置
pll 锁相环, 倍频
串口工作原理
串口工作核心图
#define ULCON0 *((volatile unsigned int *)0XE2900000)
volatile 关键字,防止编译器做优化,每次读取寄存器的值,都是重新读取寄存器。
//start.s
AREA start_main,CODE, READONLY
ENTRY
IMPORT uart_test
START
B uart_test
END
//uart.c
#define ULCON0 *((volatile unsigned int *)0XE2900000)
#define UCON0 *((volatile unsigned int *)0XE2900004)
#define UTRSTAT0 *((volatile unsigned int *)0XE2900010)
#define UTXH0 *((volatile unsigned int *)0XE2900020)
#define URXH0 *((volatile unsigned int *)0XE2900024)
#define UBRDIV0 *((volatile unsigned int *)0XE2900028)
#define UDIVSLOT0 *((volatile unsigned int *)0XE290002C)
#define GPACON0 *((volatile unsigned int *)0XE0200000)
void uart_init(void)
{
//串口管脚设置成功能态
GPACON0 = 0x22;
//设置8 N 1
ULCON0 = 0X3;
//设置轮询工作模式
UCON0 = 0X5;
//设置波特率
UBRDIV0 = 34;
UDIVSLOT0 = 0XDDDD;
}
char uart_getchar(void)
{
char ch;
//如果有数据到达,状态寄存器第0位置1
//判断状态位是否为1,决定读接收缓冲寄存器,读到的值作为函数的返回值
while (!(UTRSTAT0 & 0x1))
;
ch = URXH0;
return ch;
}
void uart_putchar(char ch)
{
//如果状态寄存器第1为置1,表示发送单元为空,可以发送数据
//把ch赋值到发送缓冲寄存器里,状态寄存器第1为置0, 自动发送,当发送完毕
while (!(UTRSTAT0 & 0X2))
;
UTXH0 = ch;
}
void uart_test(void)
{
char ch;
uart_init();
uart_putchar('a');
uart_putchar('b');
uart_putchar('c');
//串口回显功能
while (1)
{
ch = uart_getchar();
uart_putchar(ch);
}
}
内存工作原理
--------------------------------------------------
NandFlash工作原理
内存是总线设备,nandflash属于非总线设备。
没有地址线, 只有数据线。
内存:总线数据, nandflash:非总线设备。
命令、地址、数据复用端口。
忙闲位。
裸板操作NandFlash的示例代码:
#define NFCONF (*(volatile unsigned int *)0xB0E00000)
#define NFCONT (*(volatile unsigned int *)0xB0E00004)
#define NFCMMD (*(volatile unsigned int *)0xB0E00008)
#define NFADDR (*(volatile unsigned int *)0xB0E0000C)
#define NFDATA (*(volatile unsigned int *)0xB0E00010)
#define NFSTAT (*(volatile unsigned int *)0xB0E00028)
#define MP0_3CON (*(volatile unsigned int *)0xE0200320)
#define PAGE_SIZE 2048
void nand_init(void)
{
//[15:12]TACLS = 1->(1) 1/133Mhz = 7.5ns
//[11:8] TWRPH0 = 1->(1+7) 7.5ns*2 = 15ns
//[7:4] TWRPH1 = 1->(1+1) 7.5ms *2 = 15ns
NFCONF |= 1<<2 | 1<< 8 | 1<< 4;
//AdrCycle [1]1=5 address cycle
NFCONF |= 1<<1;
//MODE [0] NAND Flash controller operating node
// 0=disable nand flash controller
// *1 = enable nand flash controller
NFCONT |= 1<<0;
//Reg_nCE0 [1] nandflash memort nRCS[0] signal control
// *0 = force nRCS[0] to low (enable chip select)
// 1 = force nRCS[0] to high(disable chip select)
NFCONT &= ~(1<<1);
//GPIO functional mux setting
// 0010 = NF_xxx
MP0_3CON = 0X22222222;
return ;
}
void nand_read_id(char id[])
{
int i;
//write read_id cmd 90th
NFCMMD = 0X90;
//write address 00h
NFADDR = 0x00;
for(i=0; i<5; i++)
{
id[i] = NFDATA;
}
return ;
}
void nand_read_page(int addr, char buf[])
{
int i;
char tmp;
//write read_page cmd 00h
NFCMMD = 0X00;
//write 5 address
NFADDR = (addr >> 0) & 0xFF;
NFADDR = (addr >> 8) & 0x7;
NFADDR = (addr >> 11) & 0xFF;
NFADDR = (addr >> 19) & 0xFF;
NFADDR = (addr >> 27) & 0x1;
//write read_page cmd 30h
NFCMMD = 0X30;
//wait for R/nB -->ready
while( (NFSTAT &(1<<0))==0 )
;
//read data 2048 bytes
for(i=0; i<PAGE_SIZE; i++)
{
buf[i] = NFDATA;
}
for (i=0; i<64; i++)
{
tmp = NFDATA;
}
return ;
}
void nand_read(int nand_addr, char *sdram_addr, int size)
{
int pages = (size -1)/PAGE_SIZE + 1;
int i;
for (i=0; i<pages; i++)
{
nand_read_page(nand_addr + i*PAGE_SIZE, sdram_addr + i*PAGE_SIZE);
}
}
uboot中操作NandFlash的示例代码:
//s3c2440_nand.c
#include <common.h>
#if 0
#define DEBUGN printf
#else
#define DEBUGN(x,args ...){}
#endif
#include <nand.h>
#include <asm/arch/s3c24x0_cpu.h>
#include <asm/arch/s3c2410.h>
#include <asm/io.h>
#define __REGb(x) (*(volatile unsigned char *)(x))
#define __REGi(x) (*(volatile unsigned int *)(x))
#define NF_BASE 0x4e000000
#define NFCONF __REGi(NF_BASE + 0x0)
#define NFCONT __REGi(NF_BASE + 0x4)
#define NFCMD __REGb(NF_BASE + 0x8)
#define NFADDR __REGb(NF_BASE + 0xc)
#define NFDATA __REGb(NF_BASE + 0x10)
#define NFMECCD0 __REGi(NF_BASE + 0x14)
#define NFMECCD1 __REGi(NF_BASE + 0x18)
#define NFSECCD __REGi(NF_BASE + 0x1C)
#define NFSTAT __REGb(NF_BASE + 0x20)
#define NFSTAT0 __REGi(NF_BASE + 0x24)
#define NFSTAT1 __REGi(NF_BASE + 0x28)
#define NFMECC0 __REGi(NF_BASE + 0x2C)
#define NFMECC1 __REGi(NF_BASE + 0x30)
#define NFSECC __REGi(NF_BASE + 0x34)
#define NFSBLK __REGi(NF_BASE + 0x38)
#define NFEBLK __REGi(NF_BASE + 0x3C)
#define S3C2440_NFCONT_nCE (1<<1)
#define S3C2440_ADDR_NALE 0x08
#define S3C2440_ADDR_NCLE 0x0c
#ifdef CONFIG_NAND_SPL
/* in the early stage of NAND flash booting, printf() is not available */
#define printf(fmt, args...)
static void nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
{
int i;
struct nand_chip *this = mtd->priv;
for (i = 0; i < len; i++)
buf[i] = readb(this->IO_ADDR_R);
}
#endif
ulong IO_ADDR_W = NF_BASE;
static void s3c2440_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
{
struct nand_chip *chip = mtd->priv;
DEBUGN("hwcontrol(): 0x%02x 0x%02x\n", cmd, ctrl);
if (ctrl & NAND_CTRL_CHANGE) {
IO_ADDR_W = NF_BASE;
if (!(ctrl & NAND_CLE))
IO_ADDR_W |= S3C2440_ADDR_NCLE;
if (!(ctrl & NAND_ALE))
IO_ADDR_W |= S3C2440_ADDR_NALE;
if (ctrl & NAND_NCE)
NFCONT &= ~ S3C2440_NFCONT_nCE;
else
NFCONT |= S3C2440_NFCONT_nCE;
}
if (cmd != NAND_CMD_NONE)
writeb(cmd, (void *)IO_ADDR_W);
}
static int s3c2440_dev_ready(struct mtd_info *mtd)
{
DEBUGN("dev_ready\n");
return(NFSTAT & 0x01);
}
#ifdef CONFIG_S3C2410_NAND_HWECC
void s3c2410_nand_enable_hwecc(struct mtd_info *mtd, int mode)
{
struct s3c2410_nand *nand = s3c2410_get_base_nand();
debugX(1, "s3c2410_nand_enable_hwecc(%p, %d)\n", mtd, mode);
writel(readl(&nand->NFCONF) | S3C2410_NFCONF_INITECC, &nand->NFCONF);
}
static int s3c2410_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
u_char *ecc_code)
{
struct s3c2410_nand *nand = s3c2410_get_base_nand();
ecc_code[0] = readb(&nand->NFECC);
ecc_code[1] = readb(&nand->NFECC + 1);
ecc_code[2] = readb(&nand->NFECC + 2);
debugX(1, "s3c2410_nand_calculate_hwecc(%p,): 0x%02x 0x%02x 0x%02x\n",
mtd , ecc_code[0], ecc_code[1], ecc_code[2]);
return 0;
}
static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat,
u_char *read_ecc, u_char *calc_ecc)
{
if (read_ecc[0] == calc_ecc[0] &&
read_ecc[1] == calc_ecc[1] &&
read_ecc[2] == calc_ecc[2])
return 0;
printf("s3c2410_nand_correct_data: not implemented\n");
return -1;
}
#endif
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();
DEBUGN("board_nand_init()\n");
writel(readl(&clk_power->CLKCON) | (1 << 4), &clk_power->CLKCON);
/* initialize hardware */
twrph0 = 4;
twrph1 =2;
tacls = 0;
cfg = ((tacls<<12)|(twrph0<<8)|(twrph1<<4));
NFCONF=cfg;
cfg = ((1<<6)|(1<<4)|(0<<1)|(1<<0));
NFCONT=cfg;
/* initialize nand_chip data structure */
nand->IO_ADDR_R = nand->IO_ADDR_W = (void *)0x4e000010;
/* read_buf and write_buf are default */
/* read_byte and write_byte are default */
/* hwcontrol always must be implemented */
nand->cmd_ctrl = s3c2440_hwcontrol;
nand->dev_ready = s3c2440_dev_ready;
nand->ecc.mode = NAND_ECC_SOFT;
DEBUGN("end of nand_init\n");
return 0;
}