Chinaunix首页 | 论坛 | 博客
  • 博客访问: 335518
  • 博文数量: 92
  • 博客积分: 2500
  • 博客等级: 少校
  • 技术积分: 960
  • 用 户 组: 普通用户
  • 注册时间: 2009-08-21 19:38
文章分类

全部博文(92)

文章存档

2010年(71)

2009年(21)

我的朋友

分类: 嵌入式

2010-06-24 10:33:08

U-BOOT移植

我的经验就是从u-boot的启动过程和目录结构入手分析,掌握重要的数据结构,完成所需的设备移植。

1.U-BOOT的启动过程

U-Boot第一阶段启动过程

cpu/arm920t/start.s 完成硬件初始化

  flash中执行的引导代码,也就是bootloader中的stage1,负责初始化硬件环境,把u-bootflash加载到RAM中去,然后跳到lib_arm/board.c中的start_armboot中去执行。
1.1.6版本的start.s流程:
 硬件环境初始化
   
  进入svc模式;关闭watch dog;屏蔽所有IRQ掩码;设置时钟频率FCLKHCLKPCLK;I/D cache;禁止MMUCACHE;配置memory control为第二阶段准备RAM空间(board/TX2440/lowlevel_init.s;
 
重定位
   
  如果当前代码不在连接指定的地址上,则需要把u-boot从当前位置拷贝到RAM指定位置中;
 
建立堆栈,堆栈是进入C函数前必须初始化的。
 
.bss
 
跳到start_armboot函数中执行。(lib_arm/board.c

 

U-Boot第二阶段启动过程

第二阶段代码:lib_arm/board.c中的start_armboot函数

start_armbootU-Boot执行的第一个C语言函数,完成系统初始化工作,进入主循环,处理用户输入的命令。这里只简要列出了主要执行的函数流程:

void start_armboot (void)
   {
      
//全局数据变量指针gd占用r8
          DECLARE_GLOBAL_DATA_PTR;
         
          /*
给全局数据变量gd安排空间*/
          gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t));
          memset ((void*)gd, 0, sizeof (gd_t));
         
         
/* 给板子数据变量gd->bd安排空间*/
          gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));
          memset (gd->bd, 0, sizeof (bd_t));
          monitor_flash_len = _bss_start - _armboot_start;//
u-boot的长度。
         
        
  /* 顺序执行init_sequence数组中的初始化函数 */
          for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
                 if ((*init_fnc_ptr)() != 0) {
                         hang ();
                 }
          }
         
         
/*配置可用的Flash */
          size = flash_init ();
      
 ……
         
/* 初始化堆空间 */
          mem_malloc_init (_armboot_start - CFG_MALLOC_LEN);
        
  /* 重新定位环境变量, */
          env_relocate ();
          /*
从环境变量中获取IP地址 */
          gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr");
          /*
以太网接口MAC 地址 */
          ……
          devices_init ();     
/* 系统外围设备初始化 */
          jumptable_init ();  //跳转表初始化
          console_init_r ();   
/* 完整地初始化控制台设备 */
          enable_interrupts (); /* 使能中断处理 */
          /* 通过环境变量初始化 */
          if ((s = getenv ("loadaddr")) != NULL) {
                  load_addr = simple_strtoul (s, NULL, 16);
          }
          /* main_loop()
循环不断执行 */
          for (;;) {
                  main_loop ();     
/* 主循环函数处理执行用户命令 -- common/main.c */
          }
   }

初始化函数序列init_sequence[]
  init_sequence[]
数组保存着基本的初始化函数指针。这些函数名称和实现的程序文件在下列注释中。
  
  init_fnc_t *init_sequence[] = {
         cpu_init,             /*
基本的处理器相关配置 -- cpu/arm920t/cpu.c */
        
board_init,           /* 基本的板级相关配置 -- board/TX2440/ TX2440.c */
         interrupt_init,       /* 初始化例外处理 -- cpu/arm920t/s3c24x0/interrupt.c */
         env_init,             /*
初始化环境变量 -- common/env_flash.c */
         init_baudrate,        /*
初始化波特率设置 -- lib_arm/board.c */
         serial_init,          /*
串口通讯设置 -- cpu/arm920t/s3c24x0/serial.c */
         console_init_f,       /*
控制台初始化阶段1 -- common/console.c */
         display_banner,       /*
打印u-boot信息 -- lib_arm/board.c */
         dram_init,            /*
配置可用的RAM -- board/ TX2440/ TX2440.c */
         display_dram_config,  /*
显示RAM的配置大小 -- lib_arm/board.c */
         NULL,
  };

 

2.U-BOOT的重要数据结构

u-boot的主要功能是用于引导OS的,但是本身也提供许多强大的功能,可以通过输入命令行来完成许多操作。所以它本身也是一个很完备的系统。u-boot的大部分操作都是围绕它自身的数据结构,这些数据结构是通用的,但是不同的板子初始化这些数据就不一样了。所以u-boot的通用代码是依赖于这些重要的数据结构的。这里说的数据结构其实就是一些全局变量。
 
 1gd 全局数据变量指针,它保存了u-boot运行需要的全局数据,类型定义:
 
 typedef struct global_data {
           bd_t  *bd;      //board data pointor
板子数据指针
           unsigned long flags; 
 //指示标志,如设备已经初始化标志等。
           unsigned long baudrate; //
串口波特率
           unsigned long have_console; /*
串口初始化标志*/
           unsigned long reloc_off;   /*
重定位偏移,就是实际定向的位置与编译连接时指定的位置之差,一般为0 */
           unsigned long env_addr; /*
环境参数地址*/
           unsigned long env_valid; /*
环境参数CRC检验有效标志 */
           unsigned long fb_base; /* base address of frame buffer */
          
 #ifdef CONFIG_VFD
           unsigned char vfd_type; /* display type */
          
 #endif
           void  **jt;  /*
跳转表,1.1.6中用来函数调用地址登记 */
          } gd_t;
  
2)bd 板子数据指针。板子很多重要的参数。 类型定义如下:    
   typedef struct bd_info {
             int   bi_baudrate;     /*
串口波特率 */
             unsigned long bi_ip_addr;   /* IP
地址 */
             unsigned char bi_enetaddr[6]; /* MAC
地址*/
             struct environment_s        *bi_env;
             ulong         bi_arch_number; /* unique id for this board */
             ulong         bi_boot_params; /*
启动参数 */
             struct    /* RAM
配置 */
             {
            ulong start;
            ulong size;
             }bi_dram[CONFIG_NR_DRAM_BANKS];
         } bd_t; 
  
3)环境变量指针 env_t *env_ptr = (env_t *)(&environment[0]);(common/env_flash.c)
  
 env_ptr指向环境参数区,系统启动时默认的环境参数environment[],定义在common/environment.c中。 

 

2.开始移植U-BOOT

2.1 修改Makefile

#Vim Makefile(我们是基于smdk2410开发板,修改配置的)

所以在smdk2410_config 下加入

TX2440_config : unconfig

       @$(MKCONFIG) $ (@:_config=) arm arm920t TX2440 NULL s3c24x0

 

在第128行:

ifeq ($(ARCH),arm)

CROSS_COMPILE = arm-linux-

 

2.2 K4S561632 型号SDRAM的配置

修改SDRAM配置,在board/TX2440/lowlevel_init.S中,

#define B6_BWSCON          (DW32) 位宽为32 bank6接的是SDRAM,确定是32位)

Bank4上接了DM9000的网卡驱动

 

通过REFCNT寄存器的设置,设置SDRAM的刷新参数

由于我所用的开发板的HCLK设置为100Mhz,根据SDRAM芯片(K4S561632数据手册上注:64ms refresh period 8K Cycle))的参数计算REFCNT寄存器的值。

算下来SDRAM的刷新周期为7.8us

计算公式如下:

R_CNT=2^11+1-HCLKMHz*SDRAM刷新周期(us=0x4f4

  #define REFCNT    0x1113  改为  #define REFCNT  0x4f4

 

增加对S3C2440的支持,2440的时钟计算公式、NAND操作和2410不太一样。

对于2440开发板,将FCLK设为400MHz,分频比为FCLKHCLKPCLK=148

 

2.3 TX2440的时钟设置和管脚设置

增加对S3C2440的支持,2440的时钟计算公式、NAND操作和2410不太一样。

对于2440开发板,将FCLK设为400MHz,分频比为FCLKHCLKPCLK=148

修改board/TX2440/TX2440.c中的board_init函数

/* S3C2440: Mpll,Upll = (2*m * Fin) / (p * 2^s)

 * m = M (the value for divider M)+ 8, p = P (the value for divider P) + 2

 */

#define S3C2440_MPLL_400MHZ     ((0x7f<<12)|(0x02<<4)|(0x01))

#define S3C2440_UPLL_48MHZ      ((0x38<<12)|(0x02<<4)|(0x02))

#define S3C2440_CLKDIV          0x05    /* FCLK:HCLK:PCLK = 1:4:8 */

int board_init (void)

{

       S3C24X0_CLOCK_POWER * const clk_power = S3C24X0_GetBase_CLOCK_POWER();

       S3C24X0_GPIO * const gpio = S3C24X0_GetBase_GPIO();

 

       /* set up the I/O ports */

       gpio->GPACON = 0x007FFFFF;(地址信号和片选信号)

       gpio->GPBCON = 0x00044555;GPB0~GPB5GPB7GPB9为输出,GPB6GPB8GPB10为输入)

       gpio->GPBUP = 0x000007FF;(GPB0~GPB10不使能)

       gpio->GPCCON = 0xAAAAAAAA;   GPC0~GPC15LCD的配置管脚)

       gpio->GPCUP = 0x0000FFFF;GPC0~GPC15不使能)

       gpio->GPDCON = 0xAAAAAAAA; GPD0~GPD15LCD的配置管脚)

       gpio->GPDUP = 0x0000FFFF;()

       gpio->GPECON = 0xAAAAAAAA; (GPE15IICSDA, GPE14IICSCL, GPE13SPICLK0, GPE12SPIMOSI0, GPE11SPIMISO0,剩下的为SD芯片配置 )

       gpio->GPEUP = 0x0000FFFF;

       gpio->GPFCON = 0x000055AA; (GPF0~GPF7EINT0~EINT7)

       gpio->GPFUP = 0x000000FF;

       gpio->GPGCON = 0xFF95FFBA; (SPI接口四大信号:MOSI, MISO, SCLK, nSS0, 其余为中断)

       gpio->GPGUP = 0x0000FFFF;()

       gpio->GPHCON = 0x002AFAAA;(串口0和串口1nCTS, nRTS, TXD,RXD,还有时钟

       gpio->GPHUP = 0x000007FF;

      

 

              /* FCLK:HCLK:PCLK = 1:4:8 */

        clk_power->CLKDIVN = S3C2440_CLKDIV;

 

        /* change to asynchronous bus mod */

        __asm__(    "mrc    p15, 0, r1, c1, c0, 0\n"    /* read ctrl register   */ 

                    "orr    r1, r1, #0xc0000000\n"      /* Asynchronous         */ 

                    "mcr    p15, 0, r1, c1, c0, 0\n"    /* write ctrl register  */ 

                    :::"r1"

                    );

 

        /* to reduce PLL lock time, adjust the LOCKTIME register */

        clk_power->LOCKTIME = 0xFFFFFF;

 

        /* configure MPLL =FCLK=400MHZ*/

        clk_power->MPLLCON = S3C2440_MPLL_400MHZ;

 

        /* some delay between MPLL and UPLL */

        delay (4000);

 

        /* configure UPLL48MHZ FOR USB*/

        clk_power->UPLLCON = S3C2440_UPLL_48MHZ;

 

        /* some delay between MPLL and UPLL */

        delay (8000);

       

        /* arch number of SMDK2440-Board */

        gd->bd->bi_arch_number = MACH_TYPE_S3C2440;

 

       /* adress of boot parameters */

       gd->bd->bi_boot_params = 0x30000100;

 

       icache_enable();

       dcache_enable();

 

       return 0;

}

  

2.3支持K9F2G08大页NAND FLASH

2.3.1 增加CFG_CMD_NAND命令

首先在配置文件include/configs/TX2440.h的宏CONFIG_COMMANDS中增加CFG_CMD_NAND (大概在82)

/*NAND flash settings*/

#define CFG_NAND_BASE        0      //无实际意义:基地址,在board_nand_init中重新定义

#define CFG_MAX_NAND_DEVICE     1   //NAND Flash设备数目为1

#define NAND_MAX_CHIPS          1   //每个NAND设备由1NADN芯片组成

修改配置文件后再编译,只有一个错误了“board_nand_init”函数未定义

 

2.3.1 添加NAND FLASH数据结构和函数

include/s3c24x0.h中增加S3C2440_NAND数据结构(168行)

/* NAND FLASH (see S3C2440 manual chapter 6) */

typedef struct {

       S3C24X0_REG32  NFCONF;

       S3C24X0_REG32  NFCONT;

       S3C24X0_REG32  NFCMD;

       S3C24X0_REG32  NFADDR;

       S3C24X0_REG32  NFDATA;

    S3C24X0_REG32  NFMECCD0;

    S3C24X0_REG32  NFMECCD1;

    S3C24X0_REG32  NFSECCD;

       S3C24X0_REG32  NFSTAT;

    S3C24X0_REG32  NFESTAT0;

    S3C24X0_REG32  NFESTAT1;

    S3C24X0_REG32  NFMECC0;

    S3C24X0_REG32  NFMECC1;

    S3C24X0_REG32  NFSECC;

       S3C24X0_REG32  NFSBLK;

    S3C24X0_REG32  NFEBLK;

} /*__attribute__((__packed__))*/ S3C2440_NAND;

 

include/s3c2410.h中仿照S3C2410_GetBase_NAND函数(96行)

定义2440的函数:

static inline S3C2440_NAND * const S3C2440_GetBase_NAND(void)

{

    return (S3C2440_NAND * const)S3C2410_NAND_BASE;

}

 

2.3.2 添加NAND FLASH 驱动

drivers目录下放置新的nand nand_legacy文件夹

并修改Makefile

 

cpu\arm920t\s3c24x0目录下放置新的nand_flash.c

include目录下放置新的nand.h

 

 

 

 

 

 

阅读(1753) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~