Reset:
ldr sp, =4096 @ 设置栈指针,以下都是C函数,调用前需要设好栈
bl disable_watch_dog @ 关闭WATCHDOG,否则CPU会不断重启
bl clock_init @ 设置MPLL,改变FCLK、HCLK、PCLK
bl init_led @ 初始化LED的GPIO管脚
bl timer0_init @ 初始化定时器0
bl init_irq @ 调用中断初始化函数,在init.c中
bl memsetup @ 设置存储控制器以使用SDRAM
bl copy_steppingstone_to_sdram @ 复制代码到SDRAM中
ldr sp, =0x34000000 @ 设置系统模式栈指针,
ldr pc, =on_sdram @ 跳到SDRAM中继续执行
on_sdram:
msr cpsr_c, #0xd2 @ 进入中断模式
ldr sp, =4096 @ 设置中断模式栈指针
msr cpsr_c, #0x5f @ 设置I-bit=0,开IRQ中断
ldr lr, =halt_loop @ 设置返回地址
ldr pc, =main @ 调用main函数
halt_loop:
b halt_loop
这里我们也是这样,注释都在code里,主要的就是将SP栈指针放到前面设置好直接调用相应的函数
/* * armboot - Startup Code for ARM920 CPU-core * * Copyright (c) 2001 Marius Gr鰃er <[email protected]> * Copyright (c) 2002 Alex Z黳ke <[email protected]> * Copyright (c) 2002 Gary Jennejohn <[email protected]> * * See file CREDITS for list of people who contributed to this * project. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA */
#include <config.h>
#include <version.h>
/*============ === 代码分析====================== * *还记得P28的config.h怎么来的么,前篇博文有提到 *mkconfig 会在include下生成config.h,最终里面指向 *#include <configs/tq2440.h>,所以接下来的很多宏定义都是 *在include/configs/tq2440.h里定义的 * *=================================================*/
/* ************************************************************************* * * Jump vector table as in table 3.1 in [1] * ************************************************************************* */
.globl _start
_start: b reset
ldr pc, _undefined_instruction
ldr pc, _software_interrupt
ldr pc, _prefetch_abort
ldr pc, _data_abort
ldr pc, _not_used
ldr pc, _irq
ldr pc, _fiq
_undefined_instruction: .word undefined_instruction
_software_interrupt: .word software_interrupt
_prefetch_abort: .word prefetch_abort
_data_abort: .word data_abort
_not_used: .word not_used
_irq: .word irq
_fiq: .word fiq
.balignl 16,0xdeadbeef
/*============ === 代码分析====================== * *P41-P57 主要设置了中断和异常的向量 *对应的处理函数其实除了IRQ,FIQ其他并没有做啥 * *=================================================*/
/* ************************************************************************* * * Startup Code (reset vector) * * do important init only if we don't start from memory! * relocate armboot to ram * setup stack * jump to second stage * ************************************************************************* */
_TEXT_BASE:
.word TEXT_BASE
.globl _armboot_start
_armboot_start:
.word _start
/* * These are defined in the board-specific linker script. */
.globl _bss_start
_bss_start:
.word __bss_start
.globl _bss_end
_bss_end:
.word _end
#ifdef CONFIG_USE_IRQ
/* IRQ stack memory (calculated at run-time) */
.globl IRQ_STACK_START
IRQ_STACK_START:
.word 0x0badc0de
/* IRQ stack memory (calculated at run-time) */
.globl FIQ_STACK_START
FIQ_STACK_START:
.word 0x0badc0de
#endif
/*============ === 代码分析====================== * *很明显想要使能IRQ/FIQ的话要在tq2440.h里 *定义这个CONFIG_USE_IRQ,不过stack的值就不是 *这个0x0badc0de,要更改成合适的 * *=================================================*/
/* * the actual reset code */
reset:
/* * set the cpu to SVC32 mode */
mrs r0,cpsr
bic r0,r0,#0x1f
orr r0,r0,#0xd3
msr cpsr,r0
/* * we do sys-critical inits only at reboot, * not when booting from ram! */
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
bl cpu_init_crit
#endif
/*============ === 代码分析====================== * *P140-P146会做一次系统更基本的初始化 *根据上面的注释可知如果是直接从RAM *启动的话不能调用cpu_init_crit 因为他会对RAM做 *清零的动作,code download到RAM结果自己清自己 *明显不行,否则程序跑到cpu_init_crit就挂了 * *=================================================*/
/* Set up the stack */
stack_setup:
ldr r0, _TEXT_BASE /* upper 128 KiB: relocated uboot */
sub r0, r0, #CFG_MALLOC_LEN /* malloc area */
sub r0, r0, #CFG_GBL_DATA_SIZE /* bdinfo */
#ifdef CONFIG_USE_IRQ
sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
#endif
sub sp, r0, #12 /* leave 3 words for abort-stack */
/*============ === 代码分析====================== * *P159-P166跟之前的裸奔一样设置好栈,为第二阶段的C准备 *这里需要注意的是,由于栈是对_TEXT_BASE往下偏移的,这就 *要求_TEXT_BASE不能为RAM的基址,如果将_TEXT_BASE设置成 *0x30000000,那再往下就不是RAM了! 这是要注意的,同时_TEXT_BASE *下还作为CFG_MALLOC_LEN的buffer,CFG_GBL_DATA_SIZE等用于 *传给linux的参数 * *=======> 为后面调用C函数要将stack_setup 搬到前面 * *=================================================*/
basic_init:
bl disable_watch_dog @ 关闭WATCHDOG,否则CPU会不断重启
bl clock_init @ 设置MPLL,改变FCLK、HCLK、PCLK
bl init_led @ 初始化LED的GPIO管脚
@bl timer0_init @ 初始化定时器0
bl init_irq @ 调用中断初始化函数,在init.c中
@bl uart0_init
@bl memsetup @ 设置存储控制器以使用SDRAM
@bl copy_steppingstone_to_sdram @ 复制代码到SDRAM中
#ifndef CONFIG_SKIP_RELOCATE_UBOOT
relocate: /* relocate U-Boot to RAM */
adr r0, _start /* r0 <- current position of code */
ldr r1, _TEXT_BASE /* test if we run from flash or RAM */
cmp r0, r1 /* don't reloc during debug */
beq clear_bss
ldr r2, _armboot_start
ldr r3, _bss_start
sub r2, r3, r2 /* r2 <- size of armboot */
#if 1
bl CopyCode2Ram
#else
add r2, r0, r2 /* r2 <- source end address */
copy_loop:
ldmia r0!, {r3-r10} /* copy from source address [r0] */
stmia r1!, {r3-r10} /* copy to target address [r1] */
cmp r0, r2 /* until source end addreee [r2] */
ble copy_loop
#endif
#endif /* CONFIG_SKIP_RELOCATE_UBOOT */
/*============ === 代码分析====================== * *P191-P211会判断此时正在跑的code是否就是指定的地址 *其中adr r0, _start的用法就是获得真正的运行地址 *比如_start会等于board/tq2440/config.mk * TEXT_BASE = 0x33F80000 *但是程序刚跑是前面的博文讲过PC是从0开始+4的,所以 *地址无关的PC减去_start偏移最后_start=0,而 *_TEXT_BASE = 0x33F80000,所以会执行后面的copy动作, *如果我们直接将code download到RAM的0x33F80000处,这部分的 *code则相当于无效 * *==========> CopyCode2Ram 同时支持nor nand 启动 * *=================================================*/
clear_bss:
ldr r0, _bss_start /* find start of bss segment */
ldr r1, _bss_end /* stop here */
mov r2, #0x00000000 /* clear */
clbss_l:str r2, [r0] /* clear loop... */
add r0, r0, #4
cmp r0, r1
ble clbss_l
ldr pc, _start_armboot
_start_armboot: .word start_armboot
/*============ === 代码分析====================== * *设置好栈后直接调用_start_armboot,由于采用的是 *ldr pc, _start_armboot ldr是赋值绝对地址,最终导致 *跑到运行地址处 * *=================================================*/
/* ************************************************************************* * * CPU_init_critical registers * * setup important registers * setup memory timing * ************************************************************************* */
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
cpu_init_crit:
/* * flush v4 I/D caches */
mov r0, #0
mcr p15, 0, r0, c7, c7, 0 /* flush v3/v4 cache */
mcr p15, 0, r0, c8, c7, 0 /* flush v4 TLB */
/* * disable MMU stuff and caches */
mrc p15, 0, r0, c1, c0, 0
bic r0, r0, #0x00002300 @ clear bits 13, 9:8 (--V- --RS)
bic r0, r0, #0x00000087 @ clear bits 7, 2:0 (B--- -CAM)
orr r0, r0, #0x00000002 @ set bit 2 (A) Align
orr r0, r0, #0x00001000 @ set bit 12 (I) I-Cache
mcr p15, 0, r0, c1, c0, 0
/* * before relocating, we have to setup RAM timing * because memory timing is board-dependend, you will * find a lowlevel_init.S in your board directory. */
mov ip, lr
bl lowlevel_init
mov lr, ip
mov pc, lr
#endif /* CONFIG_SKIP_LOWLEVEL_INIT */
/* ************************************************************************* * * Interrupt handling * ************************************************************************* */
@
@ IRQ stack frame.
@
#define S_FRAME_SIZE 72
#define S_OLD_R0 68
#define S_PSR 64
#define S_PC 60
#define S_LR 56
#define S_SP 52
#define S_IP 48
#define S_FP 44
#define S_R10 40
#define S_R9 36
#define S_R8 32
#define S_R7 28
#define S_R6 24
#define S_R5 20
#define S_R4 16
#define S_R3 12
#define S_R2 8
#define S_R1 4
#define S_R0 0
#define MODE_SVC 0x13
#define I_BIT 0x80
/* * use bad_save_user_regs for abort/prefetch/undef/swi ... * use irq_save_user_regs / irq_restore_user_regs for IRQ/FIQ handling */
.macro bad_save_user_regs
sub sp, sp, #S_FRAME_SIZE
stmia sp, {r0 - r12} @ Calling r0-r12
ldr r2, _armboot_start
sub r2, r2, #(CONFIG_STACKSIZE+CFG_MALLOC_LEN)
sub r2, r2, #(CFG_GBL_DATA_SIZE+8) @ set base 2 words into abort stack
ldmia r2, {r2 - r3} @ get pc, cpsr
add r0, sp, #S_FRAME_SIZE @ restore sp_SVC
add r5, sp, #S_SP
mov r1, lr
stmia r5, {r0 - r3} @ save sp_SVC, lr_SVC, pc, cpsr
mov r0, sp
.endm
.macro irq_save_user_regs
sub sp, sp, #S_FRAME_SIZE
stmia sp, {r0 - r12} @ Calling r0-r12
add r8, sp, #S_PC
stmdb r8, {sp, lr}^ @ Calling SP, LR
str lr, [r8, #0] @ Save calling PC
mrs r6, spsr
str r6, [r8, #4] @ Save CPSR
str r0, [r8, #8] @ Save OLD_R0
mov r0, sp
.endm
.macro irq_restore_user_regs
ldmia sp, {r0 - lr}^ @ Calling r0 - lr
mov r0, r0
ldr lr, [sp, #S_PC] @ Get PC
add sp, sp, #S_FRAME_SIZE
subs pc, lr, #4 @ return & move spsr_svc into cpsr
.endm
.macro get_bad_stack
ldr r13, _armboot_start @ setup our mode stack
sub r13, r13, #(CONFIG_STACKSIZE+CFG_MALLOC_LEN)
sub r13, r13, #(CFG_GBL_DATA_SIZE+8) @ reserved a couple spots in abort stack
str lr, [r13] @ save caller lr / spsr
mrs lr, spsr
str lr, [r13, #4]
mov r13, #MODE_SVC @ prepare SVC-Mode
@ msr spsr_c, r13
msr spsr, r13
mov lr, pc
movs pc, lr
.endm
.macro get_irq_stack @ setup IRQ stack
ldr sp, IRQ_STACK_START
.endm
.macro get_fiq_stack @ setup FIQ stack
ldr sp, FIQ_STACK_START
.endm
/* * exception handlers */
.align 5
undefined_instruction:
get_bad_stack
bad_save_user_regs
bl do_undefined_instruction
.align 5
software_interrupt:
get_bad_stack
bad_save_user_regs
bl do_software_interrupt
.align 5
prefetch_abort:
get_bad_stack
bad_save_user_regs
bl do_prefetch_abort
.align 5
data_abort:
get_bad_stack
bad_save_user_regs
bl do_data_abort
.align 5
not_used:
get_bad_stack
bad_save_user_regs
bl do_not_used
#ifdef CONFIG_USE_IRQ
.align 5
irq:
get_irq_stack
irq_save_user_regs
bl do_irq
irq_restore_user_regs
.align 5
fiq:
get_fiq_stack
/* someone ought to write a more effiction fiq_save_user_regs */
irq_save_user_regs
bl do_fiq
irq_restore_user_regs
#else
.align 5
irq:
get_bad_stack
bad_save_user_regs
bl do_irq
.align 5
fiq:
get_bad_stack
bad_save_user_regs
bl do_fiq
#endif
其中移除汇编相关watchdog 时钟即中断,改成C,在board/tq2440新建 basic_init.c,把之前的code扔进去即可,同时修改该目录下的makefile
COBJS := tq2440.o flash.o basic_init.o
同时修改该目录下的U-boot.lds使其放置在前面
SECTIONS
{
. = 0x00000000;
. = ALIGN(4);
.text :
{
cpu/arm920t/start.o (.text)
board/tq2440/basic_init.o (.text)
而basic_init.c 如下:
/* * init.c: 进行一些初始化 */
#define WTCON (*(volatile unsigned long *)0x53000000)
#define CLKDIVN (*(volatile unsigned long *)0x4c000014)
#define GSTATUS1 (*(volatile unsigned long *)0x560000B0)
#define MPLLCON (*(volatile unsigned long *)0x4c000004)
#define MEM_CTL_BASE 0x48000000
#define GPBCON (*(volatile unsigned long *)0x56000010)
#define GPBDAT (*(volatile unsigned long *)0x56000014)
#define INTMSK (*(volatile unsigned long *)0x4A000008)
void disable_watch_dog(void);
void clock_init(void);
void memsetup(void);
void copy_steppingstone_to_sdram(void);
void init_led(void);
void timer0_init(void);
void init_irq(void);
/* * 关闭WATCHDOG,否则CPU会不断重启 */
void disable_watch_dog(void)
{
WTCON = 0; // 关闭WATCHDOG很简单,往这个寄存器写0即可
}
#define S3C2410_MPLL_200MHZ ((0x5c<<12)|(0x04<<4)|(0x00))
#define S3C2440_MPLL_200MHZ ((0x5c<<12)|(0x01<<4)|(0x02))
#define S3C2440_MPLL_400MHZ ((0x5c<<12)|(0x01<<4)|(0x01))
/* * 对于MPLLCON寄存器,[19:12]为MDIV,[9:4]为PDIV,[1:0]为SDIV * 有如下计算公式: * S3C2410: MPLL(FCLK) = (m * Fin)/(p * 2^s) * S3C2440: MPLL(FCLK) = (2 * m * Fin)/(p * 2^s) * 其中: m = MDIV + 8, p = PDIV + 2, s = SDIV * 对于本开发板,Fin = 12MHz * 设置CLKDIVN,令分频比为:FCLK:HCLK:PCLK=1:4:8, * FCLK=400MHz,HCLK=100MHz,PCLK=50MHz */
void clock_init(void)
{
// LOCKTIME = 0x00ffffff; // 使用默认值即可
CLKDIVN = 0x05; // FCLK:HCLK:PCLK=1:4:8, HDIVN=2,PDIVN=1
/* 如果HDIVN非0,CPU的总线模式应该从“fast bus mode”变为“asynchronous bus mode” */
__asm__(
"mrc p15, 0, r1, c1, c0, 0\n" /* 读出控制寄存器 */
"orr r1, r1, #0xc0000000\n" /* 设置为“asynchronous bus mode” */
"mcr p15, 0, r1, c1, c0, 0\n" /* 写入控制寄存器 */
);
/* 判断是S3C2410还是S3C2440 */
if ((GSTATUS1 == 0x32410000) || (GSTATUS1 == 0x32410002))
{
MPLLCON = S3C2410_MPLL_200MHZ; /* 现在,FCLK=200MHz,HCLK=100MHz,PCLK=50MHz */
}
else
{
MPLLCON = S3C2440_MPLL_400MHZ; /* 现在,FCLK=400MHz,HCLK=100MHz,PCLK=50MHz */
}
//UPLLCON = ???? ; // have not set yat
}
/* * 设置存储控制器以使用SDRAM */
void memsetup(void)
{
volatile unsigned long *p = (volatile unsigned long *)MEM_CTL_BASE;
/* 这个函数之所以这样赋值,而不是像前面的实验(比如mmu实验)那样将配置值 * 写在数组中,是因为要生成”位置无关的代码”,使得这个函数可以在被复制到 * SDRAM之前就可以在steppingstone中运行 */
/* 存储控制器13个寄存器的值 */
p[0] = 0x22011110; //BWSCON
p[1] = 0x00000700; //BANKCON0
p[2] = 0x00000700; //BANKCON1
p[3] = 0x00000700; //BANKCON2
p[4] = 0x00000700; //BANKCON3
p[5] = 0x00000700; //BANKCON4
p[6] = 0x00000700; //BANKCON5
p[7] = 0x00018005; //BANKCON6
p[8] = 0x00018005; //BANKCON7
/* REFRESH = 0x008c0000 + R_CNT * R_CNT = 2^11 +1 - HCLK(MHZ)*SDRAM_REF_TIME(us , 7.8125) * HCLK=12MHz: 0x008C07A3, * HCLK=100MHz: 0x008C04f4 */
p[9] = 0x008C04f4;
p[10] = 0x000000B1; //BANKSIZE
p[11] = 0x00000030; //MRSRB6
p[12] = 0x00000030; //MRSRB7
}
void copy_steppingstone_to_sdram(void)
{
unsigned int *pdwSrc = (unsigned int *)0;
unsigned int *pdwDest = (unsigned int *)0x30000000;
while (pdwSrc < (unsigned int *)4096)
{
*pdwDest = *pdwSrc;
pdwDest++;
pdwSrc++;
}
}
/* * LED1-4对应GPB5、GPB6、GPB7、GPB8 */
#define GPB5_out (1<<(5*2)) // LED1
#define GPB6_out (1<<(6*2)) // LED2
#define GPB7_out (1<<(7*2)) // LED3
#define GPB8_out (1<<(8*2)) // LED4
/* * K1-K4对应GPG11、GPG3、GPF2、GPF3 */
#define GPG11_eint (2<<(11*2)) // K1,EINT19
#define GPG3_eint (2<<(3*2)) // K2,EINT11
#define GPF3_eint (2<<(3*2)) // K3,EINT3
#define GPF2_eint (2<<(2*2)) // K4,EINT2
void init_led(void)
{
GPBCON = GPB5_out | GPB6_out | GPB7_out | GPB8_out ;
GPBDAT = ~0;
//GPBDAT &= ~(1<<5);
}
/* * Timer input clock Frequency = PCLK / {prescaler value+1} / {divider value} * {prescaler value} = 0~255 * {divider value} = 2, 4, 8, 16 * 本实验的Timer0的时钟频率=100MHz/(99+1)/(16)=62500Hz * 设置Timer0 0.5秒钟触发一次中断: */
/*void timer0_init(void) { TCFG0 = 99; // 预分频器0 = 99 TCFG1 = 0x03; // 选择16分频 TCNTB0 = 31250; // 0.5秒钟触发一次中断 TCON |= (1<<1); // 手动更新 TCON = 0x09; // 自动加载,清“手动更新”位,启动定时器0 }*/
/* * 定时器0中断使能 */
void init_irq(void)
{
INTMSK = ~(0);
}
/*#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 } */
/***************** nand init *************************/
自此,第一阶段的移植就完成了。
3.现在开始移植第二阶段,在start_armboot()先依次初始化:
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,
};
cpu_init()啥都没做,忽略,board_init()里面删除关于时钟即可,因为我们再前面第一阶段已经完成了。
/* 判断是S3C2410还是S3C2440 */
if ((GSTATUS1 == 0x32410000) || (GSTATUS1 == 0x32410002))
{
gd->bd->bi_arch_number = MACH_TYPE_SMDK2410;
}
else
{
gd->bd->bi_arch_number = MACH_TYPE_TQ2440;
}
MACH_TYPE_TQ2440自己在相应定义即可。init_baudrate()保持原有,即 115200波特率 8位数据位 无校验 一位停止位。这里最重要的是先调通serial_init(),这样就可以看到打印了,最终call的是serial_setbrg(),里面有个设置波特率的寄存器的公式是reg = get_PCLK() / (16 * gd->baudrate) - 1; 但是2410和2440的get_PCLK(),get_HCLK()是不一样的,要区分对待,在u-boot-1.1.6\cpu\arm920t\s3c24x0\speed.c如下:
static ulong get_PLLCLK(int pllreg)
{
S3C24X0_CLOCK_POWER * const clk_power = S3C24X0_GetBase_CLOCK_POWER();
ulong r, m, p, s;
if (pllreg == MPLL)
r = clk_power->MPLLCON;
else if (pllreg == UPLL)
r = clk_power->UPLLCON;
else
hang();
m = ((r & 0xFF000) >> 12) + 8;
p = ((r & 0x003F0) >> 4) + 2;
s = r & 0x3;
if(MACH_TYPE_SMDK2410 == gd->bd->bi_arch_number )
return((CONFIG_SYS_CLK_FREQ * m) / (p << s));
else if (MACH_TYPE_TQ2440 == gd->bd->bi_arch_number)
return((CONFIG_SYS_CLK_FREQ * m*2) / (p << s));
else
hang ();
}
/* return FCLK frequency */
ulong get_FCLK(void)
{
return(get_PLLCLK(MPLL));
}
/* return HCLK frequency */
ulong get_HCLK(void)
{
unsigned long clkdiv;
unsigned long camdiv;
int hdiv = 1;
S3C24X0_CLOCK_POWER * const clk_power = S3C24X0_GetBase_CLOCK_POWER();
if(MACH_TYPE_SMDK2410 == gd->bd->bi_arch_number )
return((clk_power->CLKDIVN & 0x2) ? get_FCLK()/2 : get_FCLK());
else if (MACH_TYPE_TQ2440 == gd->bd->bi_arch_number)
{
clkdiv = clk_power->CLKDIVN;
camdiv = clk_power->CAMDIVN;
/* work out clock scalings */
switch (clkdiv & S3C2440_CLKDIVN_HDIVN_MASK) {
case S3C2440_CLKDIVN_HDIVN_1:
hdiv = 1;
break;
case S3C2440_CLKDIVN_HDIVN_2:
hdiv = 2;
break;
case S3C2440_CLKDIVN_HDIVN_4_8:
hdiv = (camdiv & S3C2440_CAMDIVN_HCLK4_HALF) ? 8 : 4;
break;
case S3C2440_CLKDIVN_HDIVN_3_6:
hdiv = (camdiv & S3C2440_CAMDIVN_HCLK3_HALF) ? 6 : 3;
break;
}
return get_FCLK() / hdiv;
}
else
hang ();
}
/* return PCLK frequency */
ulong get_PCLK(void)
{
unsigned long clkdiv;
unsigned long camdiv;
int hdiv = 1;
S3C24X0_CLOCK_POWER * const clk_power = S3C24X0_GetBase_CLOCK_POWER();
if(MACH_TYPE_SMDK2410 == gd->bd->bi_arch_number )
return((clk_power->CLKDIVN & 0x1) ? get_HCLK()/2 : get_HCLK());
else if (MACH_TYPE_TQ2440 == gd->bd->bi_arch_number)
{
clkdiv = clk_power->CLKDIVN;
camdiv = clk_power->CAMDIVN;
/* work out clock scalings */
switch (clkdiv & S3C2440_CLKDIVN_HDIVN_MASK) {
case S3C2440_CLKDIVN_HDIVN_1:
hdiv = 1;
break;
case S3C2440_CLKDIVN_HDIVN_2:
hdiv = 2;
break;
case S3C2440_CLKDIVN_HDIVN_4_8:
hdiv = (camdiv & S3C2440_CAMDIVN_HCLK4_HALF) ? 8 : 4;
break;
case S3C2440_CLKDIVN_HDIVN_3_6:
hdiv = (camdiv & S3C2440_CAMDIVN_HCLK3_HALF) ? 6 : 3;
break;
}
return get_FCLK() / hdiv / ((clkdiv & S3C2440_CLKDIVN_PDIVN)? 2:1);
}
else
hang ();
}
现在怎么测试serial呢,直接烧入到nor flash即可,但由于tq2440自带的uboot就在nor,还得靠他到时候把code 烧入到nand flash的,所以只能download到SDRAM………………..,在此之前得稍微修改一下start.S code,禁止bl cpu_init_crit,使能bl memsetup 然后利用DNW download 到SDRAM地址为TEXT_BASE = 0x33F80000处即可,DNW有个选项:
[7] Download Program (uCOS-II or TQ2440_Test) to SDRAM and Run it
接着会有打印出来:
准备下载文件: Z:\u-boot-1.1.6\u-boot.bin
下载属性:
文件大小 : 94816 (0MB)
起始地址 : 0x33f80000
结束地址 : 0x33f97260
开始下载...
Now, Downloading [ADDRESS:33f80000h,TOTAL:94826]
RECEIVED FILE SIZE: 94826 (92KB/S, 1S)
## Starting application at 0x33F80000 ...
U-Boot 1.1.6 (May 16 2015 - 11:11:55)
DRAM: 64 MB
Flash: 512 kB
*** Warning - bad CRC, using default environment
In: serial
Out: serial
Err: serial
SMDK2410 #
串口有信息出来就一切都好办了……….. ^_^
4. nor nand flash 移植
nor的很简单 tq2440的板子与tq2440.h里的CONFIG_AMD_LV800匹配,故直接更改这个即可,当然还要更改size,而且还要注意环境变量的存储,/* CFG_ENV_ADDR + CFG_ENV_SIZE <= PHYS_FLASH_SIZE*/
#if 0
#define CONFIG_AMD_LV400 1 /* uncomment this if you have a LV400 flash */
#endif
#define CONFIG_AMD_LV800 1 /* uncomment this if you have a LV800 flash */
#define CFG_MAX_FLASH_BANKS 1 /* max number of memory banks */
#ifdef CONFIG_AMD_LV800
#define PHYS_FLASH_SIZE 0x200000 //0x00100000 /* 1MB */
#define CFG_MAX_FLASH_SECT (19) /* max number of sectors on one chip */
/* CFG_ENV_ADDR + CFG_ENV_SIZE <= PHYS_FLASH_SIZE*/
#define CFG_ENV_ADDR (CFG_FLASH_BASE + 0x01E0000) /* addr of environment */
#define CFG_ENV_SIZE 0x10000 /* Total Size of Environment Sector */
继续看start_armboot()下文有:
#if (CONFIG_COMMANDS & CFG_CMD_NAND)
puts ("NAND: ");
nand_init(); /* go init the NAND */
#endif
所以启动nand的支持只要在tq2440.h增加nand cmd即可:
#define CONFIG_COMMANDS \
(CONFIG_CMD_DFL | \
CFG_CMD_CACHE | \
CFG_CMD_NAND | \
/*CFG_CMD_EEPROM |*/ \
/*CFG_CMD_I2C |*/ \
/*CFG_CMD_USB |*/ \
CFG_CMD_REGINFO | \
CFG_CMD_DATE | \
CFG_CMD_ELF)
继续进入nand_init() –> nand_init_chip() –> board_nand_init(nand) ,发现这个board_nand_init(nand)找不到 为extern,没错 这个要我们自己实现! 查看该nand结构体:
struct nand_chip {
void __iomem *IO_ADDR_R;
void __iomem *IO_ADDR_W;
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);
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);
void (*cmdfunc)(struct mtd_info *mtd, unsigned command, int column, int page_addr);
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;
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;
};
主要的是填充必要的指针或者说指向具体的相关寄存器地址,后面有uboot自带的nand driver完成。由于nand的寄存器是跟IC 有关的,实现的board_nand_init(nand)就在u-boot-1.1.6\cpu\arm920t\s3c24x0下实现,在u-boot-1.1.6\cpu\arm920t\s3c24x0下新建文件nand_flash.c,code如下:
/* * Nand flash interface of s3c2410/s3c2440, by www.arm9.net * Changed from drivers/mtd/nand/s3c2410.c of kernel 2.6.13 */
#include <common.h>
#if (CONFIG_COMMANDS & CFG_CMD_NAND) && !defined(CFG_NAND_LEGACY)
#include <s3c2410.h>
#include <nand.h>
DECLARE_GLOBAL_DATA_PTR;
#define S3C2410_NFSTAT_READY (1<<0)
#define S3C2410_NFCONF_nFCE (1<<11)
#define S3C2440_NFSTAT_READY (1<<0)
#define S3C2440_NFCONT_nFCE (1<<1)
/* select chip, for s3c2410 */
static void s3c2410_nand_select_chip(struct mtd_info *mtd, int chip)
{
S3C2410_NAND * const s3c2410nand = S3C2410_GetBase_NAND();
if (chip == -1)
{
s3c2410nand->NFCONF |= S3C2410_NFCONF_nFCE;
}
else
{
s3c2410nand->NFCONF &= ~S3C2410_NFCONF_nFCE;
}
}
/* command and control functions, for s3c2410 * * Note, these all use tglx's method of changing the IO_ADDR_W field * to make the code simpler, and use the nand layer's code to issue the * command and address sequences via the proper IO ports. * */
static void s3c2410_nand_hwcontrol(struct mtd_info *mtd, int cmd)
{
S3C2410_NAND * const s3c2410nand = S3C2410_GetBase_NAND();
struct nand_chip *chip = mtd->priv;
switch (cmd)
{
case NAND_CTL_SETNCE:
case NAND_CTL_CLRNCE:
printf("%s: called for NCE\n", __FUNCTION__);
break;
case NAND_CTL_SETCLE:
chip->IO_ADDR_W = (void *)&s3c2410nand->NFCMD;
break;
case NAND_CTL_SETALE:
chip->IO_ADDR_W = (void *)&s3c2410nand->NFADDR;
break;
default:
chip->IO_ADDR_W = (void *)&s3c2410nand->NFDATA;
break;
}
}
/* s3c2410_nand_devready() * * returns 0 if the nand is busy, 1 if it is ready */
static int s3c2410_nand_devready(struct mtd_info *mtd)
{
S3C2410_NAND * const s3c2410nand = S3C2410_GetBase_NAND();
return (s3c2410nand->NFSTAT & S3C2410_NFSTAT_READY);
}
/* select chip, for s3c2440 */
static void s3c2440_nand_select_chip(struct mtd_info *mtd, int chip)
{
S3C2440_NAND * const s3c2440nand = S3C2440_GetBase_NAND();
if (chip == -1)
{
s3c2440nand->NFCONT |= S3C2440_NFCONT_nFCE;
}
else
{
s3c2440nand->NFCONT &= ~S3C2440_NFCONT_nFCE;
}
}
/* command and control functions */
static void s3c2440_nand_hwcontrol(struct mtd_info *mtd, int cmd)
{
S3C2440_NAND * const s3c2440nand = S3C2440_GetBase_NAND();
struct nand_chip *chip = mtd->priv;
switch (cmd)
{
case NAND_CTL_SETNCE:
case NAND_CTL_CLRNCE:
printf("%s: called for NCE\n", __FUNCTION__);
break;
case NAND_CTL_SETCLE:
chip->IO_ADDR_W = (void *)&s3c2440nand->NFCMD;
break;
case NAND_CTL_SETALE:
chip->IO_ADDR_W = (void *)&s3c2440nand->NFADDR;
break;
default:
chip->IO_ADDR_W = (void *)&s3c2440nand->NFDATA;
break;
}
}
/* s3c2440_nand_devready() * * returns 0 if the nand is busy, 1 if it is ready */
static int s3c2440_nand_devready(struct mtd_info *mtd)
{
S3C2440_NAND * const s3c2440nand = S3C2440_GetBase_NAND();
return (s3c2440nand->NFSTAT & S3C2440_NFSTAT_READY);
}
/* * Nand flash hardware initialization: * Set the timing, enable NAND flash controller */
static void s3c24x0_nand_inithw(void)
{
S3C2410_NAND * const s3c2410nand = S3C2410_GetBase_NAND();
S3C2440_NAND * const s3c2440nand = S3C2440_GetBase_NAND();
#define TACLS 0
#define TWRPH0 4
#define TWRPH1 2
if (gd->bd->bi_arch_number == MACH_TYPE_SMDK2410)
{
/* Enable NAND flash controller, Initialize ECC, enable chip select, Set flash memory timing */
s3c2410nand->NFCONF =
(1<<15)|(1<<12)|(1<<11)|(TACLS<<8)|(TWRPH0<<4)|(TWRPH1<<0);
}
else
{
/* 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);
}
}
/* * Called by drivers/nand/nand.c, initialize the interface of nand flash */
void board_nand_init(struct nand_chip *chip)
{
S3C2410_NAND * const s3c2410nand = S3C2410_GetBase_NAND();
S3C2440_NAND * const s3c2440nand = S3C2440_GetBase_NAND();
s3c24x0_nand_inithw();
if (gd->bd->bi_arch_number == MACH_TYPE_SMDK2410)
{
chip->IO_ADDR_R = (void *)&s3c2410nand->NFDATA;
chip->IO_ADDR_W = (void *)&s3c2410nand->NFDATA;
chip->hwcontrol = s3c2410_nand_hwcontrol;
chip->dev_ready = s3c2410_nand_devready;
chip->select_chip = s3c2410_nand_select_chip;
chip->options = 0;
}
else
{
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;
chip->select_chip = s3c2440_nand_select_chip;
chip->options = 0;
}
chip->eccmode = NAND_ECC_SOFT;
}
#endif
修改当前目录makefile加入编译:
COBJS = i2c.o interrupts.o serial.o speed.o \
usb_ohci.o nand_flash.o
make进行编译 发现依次有如下等警告:
u-boot-1.1.6/include/linux/mtd/nand.h:412: error: `NAND_MAX_CHIPS' undeclared here (not in a function)
nand.c:38: error: `CFG_NAND_BASE' undeclared here (not in a function)
在tq2440.h增加如下申明:
/* nand configure */
#define CFG_MAX_NAND_DEVICE 1
#define NAND_MAX_CHIPS 1
#define CFG_NAND_BASE 0
5.环境变量设置,由于环境的设置依赖存储介质,所以放在nor 或nand设置后来设置,搜索env_init()发现在env_flash.c env_nand.c 都有实现 怎么区分呢? 可以查看其code前面几行有相应的宏加以判断:
#if defined(CFG_ENV_IS_IN_NAND) /* Environment is in Nand Flash */
#if defined(CFG_ENV_IS_IN_FLASH) /* Environment is in Flash */
所以由于以后要在nand启动,故在tq2440.h设置环境变量放置在nand:
/#define CFG_ENV_IS_IN_FLASH 1
#define CFG_ENV_IS_IN_NAND 1
当然 make编译会出现error:
u-boot-1.1.6/include/environment.h:74:4: 错误: #error "Need to define CFG_ENV_OFFSET when using CFG_ENV_IS_IN_NAND"
//因为nor的偏移地址是直接赋值
#define CFG_ENV_ADDR (CFG_FLASH_BASE + 0x01E0000) /* addr of environment */
//nand 用宏标记
#define CFG_ENV_OFFSET 0x40000 //256k,it enouph fo store uboot
再次make编译download到指定SDRAM地址如下打印:
准备下载文件: Z:\u-boot-1.1.6\u-boot.bin
下载属性:
文件大小 : 131920 (0MB)
起始地址 : 0x33f80000
结束地址 : 0x33fa0350
开始下载...
Now, Downloading [ADDRESS:33f80000h,TOTAL:131930]
RECEIVED FILE SIZE: 131930 (128KB/S, 1S)
## Starting application at 0x33F80000 ...
U-Boot 1.1.6 (May 16 2015 - 12:27:23)
DRAM: 64 MB
Flash: 2 MB
NAND: 256 MiB
*** Warning - bad CRC or NAND, using default environment
In: serial
Out: serial
Err: serial
[TQ2440 ]#
测试是否支持nand:
[TQ2440 ]# help
nand - NAND sub-system
nboot - boot from NAND device
[TQ2440 ]# nand info
Device 0: NAND 256MiB 3,3V 8-bit, sector size 128 KiB
[TQ2440 ]# nand dump 0 10
Page 00000000 dump:
06 00 00 ea fe ff ff ea fe ff ff ea fe ff ff ea
fe ff ff ea fe ff ff ea 11 00 00 ea fe ff ff ea
01 da a0 e3 18 00 00 eb 1b 00 00 eb 58 00 00 eb
5b 00 00 eb 68 00 00 eb 70 00 00 eb 30 00 00 eb
4a 00 00 eb 0d d3 a0 e3 28 f0 9f e5 d2 f0 21 e3
01 da a0 e3 5f f0 21 e3 1c e0 9f e5 1c f0 9f e5
fe ff ff ea 04 e0 4e e2 ff 5f 2d e9 10 e0 9f e5
10 f0 9f e5 ff 9f fd e8 4c 00 00 30 60 00 00 30
ac 02 00 30 74 00 00 30 4c 02 00 30 00 20 a0 e3
53 34 a0 e3 00 20 83 e5 0e f0 a0 e1 13 c3 a0 e3
========
[TQ2440 ]# nand erase 0 10
NAND erase: device 0 offset 0x0, size 0x10
Erasing at 0x0 -- 819200% complete.
OK
============
[TQ2440 ]# nand dump 0 10
Page 00000000 dump:
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
测试 OK
6.网口设置,由于我的是笔记本连WIFI 板子只有网口,所以暂时先不移植网络部分~~
7.同时支持nor nand启动:
现在最后一步就是支持从nand启动了,之前的测试都是直接从SDRAM download的,现在要同时支持从nor和nand启动,先enable cpu_init_crit disable memsetup
先实现nand驱动,在u-boot-1.1.6\board\tq2440新建nand_for_copy.c:
#include <asm-arm/types.h>
#include <s3c2400.h>
#include <s3c24x0.h>
#define GSTATUS1 (*(volatile unsigned int *)0x560000B0)
#define BUSY 1
#define NAND_SECTOR_SIZE 512
#define NAND_BLOCK_MASK (NAND_SECTOR_SIZE - 1)
#define NAND_SECTOR_SIZE_LP 2048
#define NAND_BLOCK_MASK_LP (NAND_SECTOR_SIZE_LP - 1)
char bLARGEBLOCK; //HJ_add 20090807
char b128MB; //HJ_add 20090807
/* 渚涘閮ㄨ皟鐢ㄧ殑鍑芥暟 */
void nand_init_ll(void);
int nand_read_ll(unsigned char *buf, unsigned long start_addr, int size);
int nand_read_ll_lp(unsigned char *buf, unsigned long start_addr, int size);
/* NAND Flash鎿嶄綔鐨勬€诲叆鍙? 瀹冧滑灏嗚皟鐢⊿3C2410鎴朣3C2440鐨勭浉搴斿嚱鏁?*/
static void nand_reset(void);
static void wait_idle(void);
static void nand_select_chip(void);
static void nand_deselect_chip(void);
static void write_cmd(int cmd);
static void write_addr(unsigned int addr);
static void write_addr_lp(unsigned int addr);
static unsigned char read_data(void);
int NF_ReadID(void); //HJ_add 20090807
/* S3C2440鐨凬AND Flash澶勭悊鍑芥暟 */
static void s3c2440_nand_reset(void);
static void s3c2440_wait_idle(void);
static void s3c2440_nand_select_chip(void);
static void s3c2440_nand_deselect_chip(void);
static void s3c2440_write_cmd(int cmd);
static void s3c2440_write_addr(unsigned int addr);
static void s3c2440_write_addr_lp(unsigned int addr);
static unsigned char s3c2440_read_data(void);
/* S3C2440鐨凬AND Flash鎿嶄綔鍑芥暟 */
/* 澶嶄綅 */
static void s3c2440_nand_reset(void)
{
s3c2440_nand_select_chip();
s3c2440_write_cmd(0xff); // 澶嶄綅鍛戒护
s3c2440_wait_idle();
s3c2440_nand_deselect_chip();
}
/* 绛夊緟NAND Flash灏辩华 */
static void s3c2440_wait_idle(void)
{
int i;
S3C2440_NAND * s3c2440nand = (S3C2440_NAND *)0x4e000000;
volatile unsigned char *p = (volatile unsigned char *)&s3c2440nand->NFSTAT;
while(!(*p & BUSY))
for(i=0; i<10; i++);
}
/* 鍙戝嚭鐗囬€変俊鍙?*/
static void s3c2440_nand_select_chip(void)
{
int i;
S3C2440_NAND * s3c2440nand = (S3C2440_NAND *)0x4e000000;
s3c2440nand->NFCONT &= ~(1<<1);
for(i=0; i<10; i++);
}
/* 鍙栨秷鐗囬€変俊鍙?*/
static void s3c2440_nand_deselect_chip(void)
{
S3C2440_NAND * s3c2440nand = (S3C2440_NAND *)0x4e000000;
s3c2440nand->NFCONT |= (1<<1);
}
/* 鍙戝嚭鍛戒护 */
static void s3c2440_write_cmd(int cmd)
{
S3C2440_NAND * s3c2440nand = (S3C2440_NAND *)0x4e000000;
volatile unsigned char *p = (volatile unsigned char *)&s3c2440nand->NFCMD;
*p = cmd;
}
/* 鍙戝嚭鍦板潃 */
static void s3c2440_write_addr(unsigned int addr)
{
int i;
S3C2440_NAND * s3c2440nand = (S3C2440_NAND *)0x4e000000;
volatile unsigned char *p = (volatile unsigned char *)&s3c2440nand->NFADDR;
*p = addr & 0xff;
for(i=0; i<10; i++);
*p = (addr >> 9) & 0xff;
for(i=0; i<10; i++);
*p = (addr >> 17) & 0xff;
for(i=0; i<10; i++);
*p = (addr >> 25) & 0xff;
for(i=0; i<10; i++);
}
/* 鍙戝嚭鍦板潃 */
static void s3c2440_write_addr_lp(unsigned int addr)
{
int i;
S3C2440_NAND * s3c2440nand = (S3C2440_NAND *)0x4e000000;
volatile unsigned char *p = (volatile unsigned char *)&s3c2440nand->NFADDR;
int col, page;
col = addr & NAND_BLOCK_MASK_LP;
page = addr / NAND_SECTOR_SIZE_LP;
*p = col & 0xff; /* Column Address A0~A7 */
for(i=0; i<10; i++);
*p = (col >> 8) & 0x0f; /* Column Address A8~A11 */
for(i=0; i<10; i++);
*p = page & 0xff; /* Row Address A12~A19 */
for(i=0; i<10; i++);
*p = (page >> 8) & 0xff; /* Row Address A20~A27 */
for(i=0; i<10; i++);
if (b128MB == 0)
*p = (page >> 16) & 0x03; /* Row Address A28~A29 */
for(i=0; i<10; i++);
}
/* 璇诲彇鏁版嵁 */
static unsigned char s3c2440_read_data(void)
{
S3C2440_NAND * s3c2440nand = (S3C2440_NAND *)0x4e000000;
volatile unsigned char *p = (volatile unsigned char *)&s3c2440nand->NFDATA;
return *p;
}
/* 鍦ㄧ涓€娆′娇鐢∟AND Flash鍓嶏紝澶嶄綅涓€涓婲AND Flash */
static void nand_reset(void)
{
s3c2440_nand_reset();
}
static void wait_idle(void)
{
s3c2440_wait_idle();
}
static void nand_select_chip(void)
{
int i;
s3c2440_nand_select_chip();
for(i=0; i<10; i++);
}
static void nand_deselect_chip(void)
{
s3c2440_nand_deselect_chip();
}
static void write_cmd(int cmd)
{
s3c2440_write_cmd(cmd);
}
static void write_addr(unsigned int addr)
{
s3c2440_write_addr(addr);
}
static void write_addr_lp(unsigned int addr)
{
s3c2440_write_addr_lp(addr);
}
static unsigned char read_data(void)
{
return s3c2440_read_data();
}
/* 鍒濆鍖朜AND Flash */
void nand_init_ll(void)
{
S3C2440_NAND * s3c2440nand = (S3C2440_NAND *)0x4e000000;
#define TACLS 0
#define TWRPH0 3
#define TWRPH1 0
/* 璁剧疆鏃跺簭 */
s3c2440nand->NFCONF = (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4);
/* 浣胯兘NAND Flash鎺у埗鍣? 鍒濆鍖朎CC, 绂佹鐗囬€?*/
s3c2440nand->NFCONT = (1<<4)|(1<<1)|(1<<0);
/* 澶嶄綅NAND Flash */
nand_reset();
}
#if 1
int NF_ReadID(void)
{
char pMID;
char pDID;
int nBuff;
char n4thcycle;
int i;
S3C2440_NAND * s3c2440nand = (S3C2440_NAND *)0x4e000000;
volatile unsigned char *p = (volatile unsigned char *)&s3c2440nand->NFADDR;
b128MB = 1;
n4thcycle = nBuff = 0;
nand_init_ll();
nand_select_chip();
write_cmd(0x90); // read id command
*p=0x00 & 0xff;
for ( i = 0; i < 100; i++ );
pMID = read_data();
pDID = read_data();
nBuff = read_data();
n4thcycle = read_data();
nand_deselect_chip();
if (pDID >= 0xA0)
{
b128MB = 0;
}
return (pDID);
}
#endif
/* 璇诲嚱鏁?*/
int nand_read_ll(unsigned char *buf, unsigned long start_addr, int size)
{
int i, j;
char dat;
S3C2440_NAND * s3c2440nand = (S3C2440_NAND *)0x4e000000;
volatile unsigned char *p = (volatile unsigned char *)&s3c2440nand->NFADDR;
if ((start_addr & NAND_BLOCK_MASK) || (size & NAND_BLOCK_MASK))
{
return -1; /* 鍦板潃鎴栭暱搴︿笉瀵归綈 */
}
/* 閫変腑鑺墖 */
nand_select_chip();
for(i=start_addr; i < (start_addr + size);)
{
/* Check Bad Block */
if(1){
/* 鍙戝嚭READ0鍛戒护 */
write_cmd(0x50);
*p = 5;
for(j=0; j<10; j++);
*p = (i >> 9) & 0xff;
for(j=0; j<10; j++);
*p = (i >> 17) & 0xff;
for(j=0; j<10; j++);
*p = (i >> 25) & 0xff;
for(j=0; j<10; j++);
wait_idle();
dat = read_data();
write_cmd(0);
/* 鍙栨秷鐗囬€変俊鍙?*/
nand_deselect_chip();
if(dat != 0xff)
i += 16384; // 1 Block = 512*32= 16384
/* Read Page */
/* 閫変腑鑺墖 */
nand_select_chip();
}
/* 鍙戝嚭READ0鍛戒护 */
write_cmd(0);
/* Write Address */
write_addr(i);
wait_idle();
for(j=0; j < NAND_SECTOR_SIZE; j++, i++)
{
*buf = read_data();
buf++;
}
}
/* 鍙栨秷鐗囬€変俊鍙?*/
nand_deselect_chip();
return 0;
}
/* 璇诲嚱鏁? * Large Page */
int nand_read_ll_lp(unsigned char *buf, unsigned long start_addr, int size)
{
int i, j;
char dat;
S3C2440_NAND * s3c2440nand = (S3C2440_NAND *)0x4e000000;
volatile unsigned char *p = (volatile unsigned char *)&s3c2440nand->NFADDR;
if ((start_addr & NAND_BLOCK_MASK_LP) || (size & NAND_BLOCK_MASK_LP))
{
return -1; /* 鍦板潃鎴栭暱搴︿笉瀵归綈 */
}
/* 閫変腑鑺墖 */
nand_select_chip();
for(i=start_addr; i < (start_addr + size);)
{
/* Check Bad Block */
if(1){
int col, page;
col = i & NAND_BLOCK_MASK_LP;
page = i / NAND_SECTOR_SIZE_LP;
/* 鍙戝嚭READ0鍛戒护 */
write_cmd(0x00);
*p = 5;
for(j=0; j<10; j++);
*p = 8;
for(j=0; j<10; j++);
*p = page & 0xff; /* Row Address A12~A19 */
for(j=0; j<10; j++);
*p = (page >> 8) & 0xff; /* Row Address A20~A27 */
for(j=0; j<10; j++);
if (b128MB == 0)
*p = (page >> 16) & 0x03; /* Row Address A28~A29 */
for(j=0; j<10; j++);
write_cmd(0x30);
wait_idle();
dat = read_data();
/* 鍙栨秷鐗囬€変俊鍙?*/
nand_deselect_chip();
if(dat != 0xff)
i += 131072; // 1 Block = 2048*64= 131072
/* Read Page */
/* 閫変腑鑺墖 */
nand_select_chip();
}
/* 鍙戝嚭READ0鍛戒护 */
write_cmd(0);
/* Write Address */
write_addr_lp(i);
write_cmd(0x30);
wait_idle();
for(j=0; j < NAND_SECTOR_SIZE_LP; j++, i++)
{
*buf = read_data();
buf++;
}
}
/* 鍙栨秷鐗囬€変俊鍙?*/
nand_deselect_chip();
return 0;
}
int bBootFrmNORFlash(void)
{
volatile unsigned int *pdw = (volatile unsigned int *)0;
unsigned int dwVal;
/* * 鏃犺鏄粠NOR Flash杩樻槸浠嶯AND Flash鍚姩锛? * 鍦板潃0澶勪负鎸囦护"b Reset", 鏈哄櫒鐮佷负0xEA00000B锛? * 瀵逛簬浠嶯AND Flash鍚姩鐨勬儏鍐碉紝鍏跺紑濮?KB鐨勪唬鐮佷細澶嶅埗鍒癈PU鍐呴儴4K鍐呭瓨涓紝 * 瀵逛簬浠嶯OR Flash鍚姩鐨勬儏鍐碉紝NOR Flash鐨勫紑濮嬪湴鍧€鍗充负0銆? * 瀵逛簬NOR Flash锛屽繀椤婚€氳繃涓€瀹氱殑鍛戒护搴忓垪鎵嶈兘鍐欐暟鎹紝 * 鎵€浠ュ彲浠ユ牴鎹繖鐐瑰樊鍒潵鍒嗚鲸鏄粠NAND Flash杩樻槸NOR Flash鍚姩: * 鍚戝湴鍧€0鍐欏叆涓€涓暟鎹紝鐒跺悗璇诲嚭鏉ワ紝濡傛灉娌℃湁鏀瑰彉鐨勮瘽灏辨槸NOR Flash */
dwVal = *pdw;
*pdw = 0x12345678;
if (*pdw != 0x12345678)
{
return 1;
}
else
{
*pdw = dwVal;
return 0;
}
}
int CopyCode2Ram(unsigned long start_addr, unsigned char *buf, int size)
{
unsigned int *pdwDest;
unsigned int *pdwSrc;
int i;
if (bBootFrmNORFlash())
{
pdwDest = (unsigned int *)buf;
pdwSrc = (unsigned int *)start_addr;
/* 浠?NOR Flash鍚姩 */
for (i = 0; i < size / 4; i++)
{
pdwDest[i] = pdwSrc[i];
}
return 0;
}
else
{
/* 鍒濆鍖朜AND Flash */
nand_init_ll();
/* 浠?NAND Flash鍚姩 */
if (NF_ReadID() == 0x76 )
nand_read_ll(buf, start_addr, (size + NAND_BLOCK_MASK)&~(NAND_BLOCK_MASK));
else
nand_read_ll_lp(buf, start_addr, (size + NAND_BLOCK_MASK_LP)&~(NAND_BLOCK_MASK_LP));
return 0;
}
}
然后在start.S:
relocate: /* relocate U-Boot to RAM */
adr r0, _start /* r0 <- current position of code */
ldr r1, _TEXT_BASE /* test if we run from flash or RAM */
cmp r0, r1 /* don't reloc during debug */
beq clear_bss
ldr r2, _armboot_start
ldr r3, _bss_start
sub r2, r3, r2 /* r2 <- size of armboot */
#if 1
bl CopyCode2Ram
#else
add r2, r0, r2 /* r2 <- source end address */
copy_loop:
ldmia r0!, {r3-r10} /* copy from source address [r0] */
stmia r1!, {r3-r10} /* copy to target address [r1] */
cmp r0, r2 /* until source end addreee [r2] */
ble copy_loop
#endif
#endif /* CONFIG_SKIP_RELOCATE_UBOOT */
即可,这里令人奇怪的是我将之前《嵌入式学习笔记005-裸奔篇之flash》的nand直接copy到nand_for_copy.c不行~~~~~~~~~~~~ 好无语 -_-
make 编译利用天嵌自带在nor flash的bootloader选择:
[1] Download u-boot or STEPLDR.nb1 or other bootloader to Nand Flash
将code烧入到nand,接着断电将拨码开关拨到nand启动选项后再上电即可:
U-Boot 1.1.6 (May 17 2015 - 10:36:23)
DRAM: 64 MB
Flash: 2 MB
NAND: 256 MiB
*** Warning - bad CRC or NAND, using default environment
In: serial
Out: serial
Err: serial
[TQ2440 ]#
至此整个uboot的移植就基本完成了,到学习linux kernel时再讲解bootcmd及传参相关的以实现uboot启动kernel~~~~~~
整个工程的源码在我的资源空间:
http://download.csdn.net/detail/fzk374470412/8708973
下载后先解压:
tar jxvf u-boot-1.1.6 -C xxx_your_path
make tq2440_config
make all