U-boot移植到mini2440上

来源:互联网 发布:有限元分析软件msc 编辑:程序博客网 时间:2024/05/15 12:40

本文章中的很多内容参考友善之臂的说明文档《mini2440之U-boot移植详细手册-20100419》,使用的U-boot的版本是u-boot-2010.03

说明:此次移植的目的不是为了制作一个功能强大的U-boot,只是做一个最简单的U_boot。该U-boot能够实现通过串口和电脑通信,可以将环境变量保存在NOR Flash中,通过TFTP服务器将使用NFS根文件系统的内核镜像从电脑拷贝到内存中执行。而且这个U-boot只支持从NOR中启动。

在移植U-Boot之前建议对U-Boot中的源码组织结构有较深的理解,可参考我的另一篇博文Bootloader介绍和Uboot源码结构


移植U-Boot的原则是,先选取与目标板相似的板子,将其的源码内容进行一份拷贝,然后从整个U-Boot的启动流程更改相应的代码。

一、移植初处理

拷贝文件

因为smdk2410与mini2440最相近,所以我们选择smdk2410作为修改对象。

  • 拷贝/board/samsung/smdk2410到/board/samsung/mini2440

  • 拷贝/include/configs/smdk2410.h到/include/configs/mini2440.h

修改顶层makefile

仿照smdk2410添加如下代码:

mini2440_config :   unconfig    @$(MKCONFIG) $(@:_config=) arm arm920t mini2440 samsung s3c24x0

初步修改/board/samsung/mini2440目录中的文件

在makefile中修改下列代码:

COBJS   := smdk2410.o flash.o

修改成:

COBJS   := mini2440.o flash.o

将文件smdk2410.c重新命名为mini2440.c

现在可以尝试着编译:

make mini2440_configmake all

二、具体代码移植

现在的思路是,跟随着U-Boot代码的启动流程进行修改和开发板相关的内容。

第一阶段(汇编语言)

.globl _start_start: b   start_code

首先运行的是标号为start_code处的代码

下面的代码是针对需要改的部分。

@   bl  coloured_LED_init@   bl  red_LED_on

因为没有必要实现LED的操作,所以直接注释掉。

s3c2440的频率设定方式与其它的有些区别,需要一些额外的寄存器如下:

#ifdef CONFIG_S3C2440#  define CLK_CTL_BASE   0x4C00000#  define MDIV_405    0x7f << 12 #  define PSDIV_405   0x21 #  define MDIV_200    0xa1 << 12 #  define PSDIV_200   0x31 #endif

需要屏蔽的子中断个数也不一样,所以要进行不同的设定:

#if defined(CONFIG_S3C2440)    ldr r1, =0x7fff    ldr r0, =INTSUBMSK    str r1, [r0]# endif

针对频率需要进行如下修改:

#if defined(CONFIG_S3C2440)/* FCLK:HCLK:PCLK = 1:4:8 */    ldr r0, =CLKDIVN    mov r1, #5    str r1, [r0]    mrc p15, 0, r1, c1, c0, 0    orr r1, r1, #0xc0000000    mcr p15, 0, r1, c1, c0, 0    mov r1, #CLK_CTL_BASE    mov r2, #MDIV_405    add r2, r2, #PSDIV_405    str r2, [r1, #0x04] #else    /* FCLK:HCLK:PCLK = 1:2:4 */    /* default FCLK is 120 MHz ! */    ldr r0, =CLKDIVN    mov r1, #3    str r1, [r0]#endif /* CONFIG_S3C2440 */ 

下面修改lowlevel_init.S,这个文件位于/board/samsung/mini2440目录下,显然和硬件关系密切。

这个文件主要是对开发板的内存地址空间硬件的挂载有关,开发板上的NOR FLASH、NAND FLASH、SDRAM、DM9000都是挂载在内存地址空间上的,所以和它们相关的内容需要在这个文件中设置。

注意:该文件不要用smdk2410中的lowlevel_init.c,应该以sbc2410x文件夹中的lowlevel_init.c为修改代码来源。否则,网络功能无法使用。

#define Trp         0x0 /* 2clk */#define REFCNT          1113

将上述代码修改成下列代码:

#define Trp         0x2 /* 4clk */#define REFCNT          1012

这一部分是和SDRAM有关的参数设定,需要查阅mini2440开发板上的SDRAM的芯片手册来获取。

    ldr     r0, =SMRDATA    ldr r1, _TEXT_BASE    sub r0, r0, r1

这段代码是进行地址的转换,当将U-Boot下载到内存中直接运行时,需要更改上面的代码。我们假定U-boot是从Flash中启动的,不进行更改。

如果需要更改能同时在SDRAM中启动,需要将上述代码进行如下修改:

    ldr     r0, =SMRDATA    ldr     r1, =lowlevel_init    sub r0, r0, r1    adr     r3, lowlevel_init    add     r0,r0,r3

其中的adr是伪代码,它会返回实际运行时,lowlevel_init的物理地址,而不是逻辑地址。

然后继续看start.s中的代码:

后面的代码是将Flash中的代码复制到内存中,清零bbs段,设置堆栈,然后跳转到C语言的入口start_armboot。

注意:如果将U-Boot烧写到NOR Flash中,跟汇编有关的代码就已经完成了,但是如果将其烧写到NAND FLASH中,需要对U-Boot.lds进行如下修改:

.text :{    /cpu/arm920t/start.o(.text)    /board/samsung/mini2440/lowlevel_init.o(.text)    *(.text)}

因为当将U-Boot下载到NAND Flash中时,只将U-Boot的前4K内容复制到芯片内部RAM中运行,采取默认连接方式时,lowlevel_init.o有可能放在4K后面的地址上,此时还没有将Flash中的代码拷贝到内存中,不能执行这部分代码。


第二阶段(C语言)

首先我们在mini2440.h中进行下列修改:

//#define CONFIG_S3C2410    1#define CONFIG_S3C2440  1

修改完之后我们的程序就无法正常编译了。因为很多文件的编译内容和头文件(配置文件)中的宏定义相关。现在,我们修改了一个和体系相关的很重要的宏,所以会出现问题。因为我们不可能找出所有与该宏定义相关的代码,我们只需要找出影响我们正常编译的宏定义处,进行修改。还有在我们需要用到的地方修改宏定义。

以下代码摘自s3c24x0_cpu.h

#elif defined(CONFIG_S3C2410)

改成下列代码:

#elif defined(CONFIG_S3C2410)||defined(CONFIG_S3C2440)

以下代码摘自s3c24x0.h

#ifdef CONFIG_S3C2410

上述代码改为:

#if defined(CONFIG_S3C2410)||defined(CONFIG_S3C2440)

这样重新编译后能够成功。

接下来继续按照程序的运行顺序进行分析更改。

init_sequence函数数组

#if defined(CONFIG_ARCH_CPU_INIT)    arch_cpu_init,      /* basic arch cpu dependent setup */#endif

对于mini2440上列代码不执行

board_init函数是和开发板紧密相关的,下面介绍对board_init的移植。

#define M_MDIV 0xA1#define M_PDIV 0x3#define M_SDIV 0x1

上列代码更改为:

#define M_MDIV 0x7f#define M_PDIV 0x2#define M_SDIV 0x1
#define U_M_MDIV    0x48#define U_M_PDIV    0x3#define U_M_SDIV    0x2

上列代码更改为:

#define U_M_MDIV    0x38#define U_M_PDIV    0x2#define U_M_SDIV    0x2

上面的更改是对CPU频率进行的更改。

对GPIO端口进行配置:

    gpio->GPACON = 0x007FFFFF;    gpio->GPBCON = 0x00295551;    gpio->GPBUP = 0x000007FF;    gpio->GPCCON = 0xAAAAAAAA;    gpio->GPCUP = 0xFFFFFFFF;    gpio->GPDCON = 0xAAAAAAAA;    gpio->GPDUP = 0xFFFFFFFF;    gpio->GPECON = 0xAAAAAAAA;    gpio->GPEUP = 0x0000FFFF;    gpio->GPFCON = 0x000055AA;    gpio->GPFUP = 0x000000FF;    gpio->GPGCON = 0xFF95FFBA;    gpio->GPGUP = 0x0000FFFF;    gpio->GPHCON = 0x002AFAAA;    gpio->GPHUP = 0x000007FF;    gpio->EXTINT1=0x22222222;    gpio->EXTINT2=0x22222222;
    gd->bd->bi_arch_number = MACH_TYPE_MINI2440;

设置开发板的ID,其实这一步是没有意义的,我们一般在R1寄存器中保存开发板ID。而且即使设置了,我们也不将其保存到tag中。

#if defined(CONFIG_USE_IRQ)    interrupt_init,     /* set up exceptions */#endif

这一部分代码是否执行取决于CONFIG_USE_IQR,这个宏如果有定义的话,需要在mini2440.h中定义,我们没有定义。

    timer_init,     /* initialize timer */

这个函数的作用是设定一个定时器,涉及到的操作是对定时器寄存器的操作,无须修改。

#ifdef CONFIG_FSL_ESDHC    get_clocks,#endif

这一部分代码是否执行取决于CONFIG_FSL_ESDHC,这个宏如果有定义的话,需要在mini2440.h中定义,我们没有定义。

    env_init,       /* initialize environment */

env_init在U_boot中多处有定义,最终选用的定义取决于mini2440.h中的宏定义。

#define CONFIG_ENV_IS_IN_FLASH  1

我们将环境变量定义保存在NOR FLASH中。

这一块代码复杂,不深究,但结构只是设置了下列两个值:

        gd->env_addr  =         gd->env_valid = 
    init_baudrate

这个函数是为了设定波特率的值,没有涉及到具体的硬件操作,只是在gd中保存波特率。

波特率的值在mini2440.h中定义,如果没有使用默认值115200

#define CONFIG_BAUDRATE     115200

我们在mini2440.h中设置了波特率为115200

serial_init

进行串口初始化,需要设定相应寄存器的值,U-Boot自带的驱动程序中包含具体的实现过程。在该函数中还调用了另一个和移植相关的重要函数:

void _serial_setbrg(const int dev_index){    struct s3c24x0_uart *uart = s3c24x0_get_base_uart(dev_index);    unsigned int reg = 0;    int i;    /* value is calculated so : (int)(PCLK/16./baudrate) -1 */    reg = get_PCLK() / (16 * gd->baudrate) - 1;    writel(reg, &uart->UBRDIV);    for (i = 0; i < 100; i++)        /* Delay */ ;}

其中的get_PCLK和移植相关,因为s3c2410和s3c2440处理时钟的寄存器差别较大,不能直接使用,需要修改。

这里需要修改的文件是speed.c

static ulong get_PLLCLK(int pllreg)中进行如下修改:

return (CONFIG_SYS_CLK_FREQ * m) / (p << s);改为:    if (pllreg == MPLL)        return (2*CONFIG_SYS_CLK_FREQ * m) / (p << s);    else if (pllreg == UPLL)        return (CONFIG_SYS_CLK_FREQ * m) / (p << s);    else        hang();

在函数ulong get_HCLK(void)中修改如下代码:

return (readl(&clk_power->CLKDIVN) & 2) ? get_FCLK() / 2 : get_FCLK();改为:return (readl(&clk_power->CLKDIVN) & 2) ? get_FCLK() / 2 : get_FCLK()/4;
console_init_f

进行控制台的初始化,其实只是设定了:

gd->have_console = 1;

这应该是表示控制台存在。

display_banner

这个只是向控制台输出一些信息,表示我们之前的工作顺利完成。

    display_banner,     /* say that we are here */#if defined(CONFIG_DISPLAY_CPUINFO)    print_cpuinfo,      /* display cpu info (and speed) */#endif

上述代码取决于CONFIG_DISPLAY_CPUINFO宏定义,需要定义在mini2440.h中,我们没有定义。

#if defined(CONFIG_DISPLAY_BOARDINFO)    checkboard,     /* display board info */#endif

上述代码取决于CONFIG_DISPLAY_BOARDINFO宏定义,需要定义在mini2440.h中,我们没有定义。

#if defined(CONFIG_HARD_I2C) || defined(CONFIG_SOFT_I2C)    init_func_i2c,#endif

同上,没有定义。

dram_init

这个必须要执行,设定SDRAM的其实地址和大小,最终需要传递给内核的。

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

上述代码在mini2440.c中定义。

#define PHYS_SDRAM_1        0x30000000 /* SDRAM Bank #1 */#define PHYS_SDRAM_1_SIZE   0x04000000 /* 64 MB */

我们在mini2440.h中定义了SDRAM的参数,需要和开发板中的内存匹配。

#if defined(CONFIG_CMD_PCI) || defined (CONFIG_PCI)    arm_pci_init,#endif

这个不执行,我们的ARM中没有PCI总线。

display_dram_config

显示内存的信息,无须修改。

现在编译生成的二进制文件就可以在开发板上运行了,但是很多功能没有实现。可以把这里当为一个检测点,要求能正常编译,控制台能正常显示内容。


第二阶段剩余部分

    mem_malloc_init (_armboot_start - CONFIG_SYS_MALLOC_LEN,            CONFIG_SYS_MALLOC_LEN);

对堆进行清零操作

#ifndef CONFIG_SYS_NO_FLASH    /* configure available FLASH banks */    display_flash_config (flash_init ());#endif /* CONFIG_SYS_NO_FLASH */

我们没有定义CONFIG_SYS_NO_FLASH

该函数中最重要的是flash_init(),display_flash_config()只是对返回值进行格式处理,然后再显示出来。

flash_init()函数在/board/samsung/mini2440/flash.c中定义,可见该函数是与开发板密切相关,移植时应当重视。

NOR FLASH基础知识:

  • NOR FLASH的读操作和内存的读操作一模一样,但是写操作复杂,需要特定的指令序。

  • 一个NOR FLASH chip由多个BLOCK组成,每个BLOCK由多个SECTION组成

  • 在写操作时,NOR FLASH只能将1转化成0,所以在写之前要进行擦除

  • 擦除的最小对象为SECTION

该部分的代码移植不介绍了,具体的参考《mini2440之U-boot移植详细手册-20100419》。

#ifdef CONFIG_VFD...#endif 

我们没有定义,不用管

#ifdef CONFIG_LCD...#endif

也没有定义,忽略

#if defined(CONFIG_CMD_NAND)...#endif 

没有定义,忽略

#if defined(CONFIG_CMD_ONENAND)...#endif

没有定义,忽略

#ifdef CONFIG_HAS_DATAFLASH...#endif

没有定义,忽略

env_relocate ();

环境变量的重定位,其中的重要内容如下:

#ifdef ENV_IS_EMBEDDED#ifndef CONFIG_RELOC_FIXUP_WORKS    env_ptr = (env_t *)((ulong)env_ptr + gd->reloc_off);#endif    DEBUGF ("%s[%d] embedded ENV at %p\n", __FUNCTION__,__LINE__,env_ptr);#else    env_ptr = (env_t *)malloc (CONFIG_ENV_SIZE);    DEBUGF ("%s[%d] malloced ENV at %p\n", __FUNCTION__,__LINE__,env_ptr);#endif

我们没有定义ENV_IS_EMBEDDED,所以环境变量的指针是指向动态分配的内存。

#ifdef CONFIG_VFD...#endif

不执行

#ifdef CONFIG_SERIAL_MULTI    serial_initialize();#endif

不执行

    gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr");

获得IP地址,储存到全局变量gd中。

stdio_init ();

一系列标准的IO口初始化,前提是定义了相应的宏。

    jumptable_init ();

其中的代码如下:

void jumptable_init(void){    gd->jt = malloc(XF_MAX * sizeof(void *));#include <_exports.h>}
#if defined(CONFIG_API)    api_init ();#endif

没有定义,不执行

console_init_r ();  /* fully init console as a device */

过程太复杂了,没有去了解,最后会在控制台输出

IN : serialOUT :serialERR :serial
#if defined(CONFIG_ARCH_MISC_INIT)    arch_misc_init ();#endif#if defined(CONFIG_MISC_INIT_R)    misc_init_r ();#endif

不执行

interrupt_init (void)

因为我们没有定义CONFIG_USE_IRQ,所以该函数是一个空的函数

#ifdef CONFIG_DRIVER_TI_EMAC...#endif 

没定义,不执行

#if defined(CONFIG_DRIVER_SMC91111) || defined (CONFIG_DRIVER_LAN91C96)...#endif 

没定义,不执行

    if ((s = getenv ("loadaddr")) != NULL) {        load_addr = simple_strtoul (s, NULL, 16);    }

这一段,根据环境变量来设置加载地址,没有定义就使用默认值。

#if defined(CONFIG_CMD_NET)    if ((s = getenv ("bootfile")) != NULL) {        copy_filename (BootFile, s, sizeof (BootFile));    }#endif

其中的CONFIG_CMD_NET是定义了的,但是不是我们自己在mini2440.h中定义的。在哪呢?
但是这里的if语句不满足,所以也是没有执行。

#ifdef BOARD_LATE_INIT    board_late_init ();#endif

没定义,不执行

#ifdef CONFIG_GENERIC_MMC    puts ("MMC:   ");    mmc_initialize (gd->bd);#endif

没定义,不执行

#ifdef CONFIG_BITBANGMII    bb_miiphy_init();#endif

没定义,不执行

#if defined(CONFIG_CMD_NET)#if defined(CONFIG_NET_MULTI)    puts ("Net:   ");#endif    eth_initialize(gd->bd);#if defined(CONFIG_RESET_PHY_R)    debug ("Reset Ethernet PHY\n");    reset_phy();#endif#endif

没有定义CONFIG_RESET_PHY_R,重要的地方就是eth_initialize(gd->bd);这一部分决定了能否使用网卡。这一部分不深入研究,请参考《mini2440之U-boot移植详细手册-20100419》。注意:《mini2440之U-boot移植详细手册-20100419》不是以smdk2410作为模板进行移植的,在移植lowlevel_init时,不应该使用smdk2410中的,否则网络功能不能执行。因为其中涉及较多很网卡有关的知识,先不在这一块深入研究,只是模仿拷贝代码。


至此已经完成了移植工作的全部内容,因为移植部分复杂,涉及更改的地方较多,所以难免会有遗漏的地方,尤其是对mini2440.h头文件相关的代码。所以建议先按上述思路移植,遇到问题之后自己检查,查看源代码是解决问题最好的方法,建议用Source Insight工具建立源码工程。哪里有问题就补哪里。

最后附上按照上面的步骤编译的U_boot.bin,实测可以在mini2440上使用。
U-Boot.bin下载地址

0 0
原创粉丝点击