展訊平台的u-boot驅動流程解析

來源:互聯網
上載者:User
最近在做展訊Android手機驅動的開發,需要能清楚U-Boot啟動過程,然後書寫相關的驅動代碼。而像lcd驅動一般會在u-boot啟動的過程中便會選擇載入某一特定的驅動,而且u-boot和kernel中的代碼大致相同,因為Android開機過程便會顯示開機logo,這涉及到顯示的部分驅動。u-boot啟動過程可以分為兩個階段,兩個階段的功能如下:
StageI:第一階段的功能(展訊為16k的彙編代碼)  硬體裝置初始化  載入U-Boot第二階段代碼到RAM空間  設定好棧  跳轉到第二階段代碼入口
StageII:第二階段的功能
  初始化本階段使用的硬體裝置  檢測系統記憶體映射  將核心從Flash讀取到RAM中  為核心設定啟動參數  調用核心

1.1.1   U-Boot啟動第一階段程式碼分析

       第一階段對應的檔案是lowlevel_init.S。       U-Boot啟動第一階段流程如下:1.   硬體裝置初始化(1)設定異常向量        其中複位異常向量的指令“b start_code”決定了U-Boot啟動後將自動跳轉到標號“start_code”處執行。(2)設定控制寄存器地址(3)關閉看門狗(4)關閉MMU,cache(5)初始化RAM控制寄存器       其中的lowlevel_init就完成了記憶體初始化的工作,由於記憶體初始化是依賴於開發板的,因此lowlevel_init的代碼一般放在board下面相應的目錄中。
       board/samsung/mini2440/lowlevel_init.o將被連結到cpu/arm920t/start.o後面,因此board/samsung/mini2440/lowlevel_init.o也在U-Boot的前4KB的代碼中。       U-Boot在NAND Flash啟動時,lowlevel_init.o將自動被讀取到CPU內部4KB的內部RAM中。因此第137~146行的代碼將從CPU內部RAM中複製寄存器的值到相應的寄存器中。       對於U-Boot在NOR Flash啟動的情況,由於U-Boot串連時確定的地址是U-Boot在記憶體中的地址,而此時U-Boot還在NOR Flash中,因此還需要在NOR Flash中讀取資料到RAM中。       由於NOR Flash的開始地址是0,而U-Boot的載入到記憶體的起始地址是TEXT_BASE,SMRDATA標號在Flash的地址就是SMRDATA-TEXT_BASE。       綜上所述,lowlevel_init的作用就是將SMRDATA開始的13個值複製給開始地址[BWSCON]的13個寄存器,從而完成了儲存控制器的設定。(6)複製U-Boot第二階段代碼到RAM       cpu/arm920t/start.S原來的代碼是只支援從NOR Flash啟動的,經過修改現在U-Boot在NOR Flash和NAND Flash上都能啟動了,實現的思路是這樣的:        bl    bBootFrmNORFlash /*  判斷U-Boot是在NAND Flash還是NOR Flash啟動  */       cmp        r0, #0          /*  r0存放bBootFrmNORFlash函數傳回值,若返回0表示NAND Flash啟動,否則表示在NOR Flash啟動  */       beq  nand_boot         /*  跳轉到NAND Flash啟動代碼  */ /*  NOR Flash啟動的代碼  */       b     stack_setup         /* 跳過NAND Flash啟動的代碼 */ nand_boot:/*  NAND Flash啟動的代碼  */ stack_setup:               /* 其他代碼 */        其中bBootFrmNORFlash函數作用是判斷U-Boot是在NAND Flash啟動還是NOR Flash啟動,若在NOR Flash啟動則返回1,否則返回0。根據ATPCS規則,函數傳回值會被存放在r0寄存器中。bBootFrmNORFlash函數後根據r0的值就可以判斷U-Boot在NAND Flash啟動還是NOR Flash啟動。bBootFrmNORFlash函數在board/samsung/mini2440/nand_read.c中定義如下:
int bBootFrmNORFlash(void){    volatile unsigned int *pdw = (volatile unsigned int *)0;    unsigned int dwVal;       dwVal = *pdw;         /* 先記錄下原來的資料 */    *pdw = 0x12345678;    if (*pdw != 0x12345678)        /* 寫入失敗,說明是在NOR Flash啟動 */    {        return 1;          }    else                                   /* 寫入成功,說明是在NAND Flash啟動 */    {        *pdw = dwVal;        /* 恢複原來的資料 */        return 0;    }}
u-boot可以選擇進入兩種不同的nand flash模式,一種是傳統的normal_nand_mode,另一種是normal_emmc_mode,這種flash標準可以將flash做成很大的儲存量。
(6)跳轉到第二階段代碼入口       ldr   pc, _start_armboot _start_armboot:   .word  start_armboot       跳轉到第二階段代碼入口start_armboot處。1.1.2   U-Boot啟動第二階段程式碼分析       start_armboot函數在lib_arm/board.c中定義,是U-Boot第二階段代碼的入口。U-Boot啟動第二階段流程如下:
選擇一個進入的模式:展訊啟動模式有fast_mode,specilal_mode,normal_mode,panic_reboot_mode等,一般啟動會進入到normal模式中。
        在分析start_armboot函數前先來看看一些重要的資料結構:(1)gd_t結構體       U-Boot使用了一個結構體gd_t來儲存全域資料區的資料,這個結構體在include/asm-arm/global_data.h中定義如下:typedef  struct     global_data {       bd_t              *bd;       unsigned long      flags;       unsigned long      baudrate;       unsigned long      have_console;      /* serial_init() was called */       unsigned long      env_addr;     /* Address  of Environment struct */       unsigned long      env_valid;    /* Checksum of Environment valid? */       unsigned long      fb_base; /* base address of frame buffer */       void              **jt;              /* jump table */} gd_t;       U-Boot使用了一個儲存在寄存器中的指標gd來記錄全域資料區的地址:#define DECLARE_GLOBAL_DATA_PTR     register volatile gd_t *gd asm ("r8")       DECLARE_GLOBAL_DATA_PTR定義一個gd_t全域資料結構的指標,這個指標存放在指定的寄存器r8中。這個聲明也避免編譯器把r8分配給其它的變數。任何想要訪問全域資料區的代碼,只要代碼開頭加入“DECLARE_GLOBAL_DATA_PTR”一行代碼,然後就可以使用gd指標來訪問全域資料區了。       根據U-Boot記憶體使用量圖中可以計算gd的值:gd = TEXT_BASE-CONFIG_SYS_MALLOC_LEN- sizeof(gd_t)(2)bd_t結構體       bd_t在include/asm-arm.u/u-boot.h中定義如下:typedef struct bd_info {    int                bi_baudrate;               /*串口通訊傳輸速率 */    unsigned long     bi_ip_addr;          /* IP地址*/    struct environment_s        *bi_env;              /*環境變數開始地址 */    ulong            bi_arch_number;      /*開發板的機器碼 */    ulong            bi_boot_params;       /*核心參數的開始地址 */    struct                         /* RAM配置資訊 */    {              ulong start;              ulong size;    }bi_dram[CONFIG_NR_DRAM_BANKS]; } bd_t;       U-Boot啟動核心時要給核心傳遞參數,這時就要使用gd_t,bd_t結構體中的資訊來設定標記列表。(3)init_sequence數組       U-Boot使用一個數組init_sequence來儲存對於大多數開發板都要執行的初始化函數的函數指標。init_sequence數組中有較多的編譯選項,去掉編譯選項後init_sequence數組如下所示:typedef int (init_fnc_t) (void); init_fnc_t *init_sequence[] = {       board_init,         /*開發板相關的配置--board/samsung/mini2440/mini2440.c */       timer_init,            /*時鐘初始化-- cpu/arm920t/s3c24x0/timer.c */       env_init,            /*初始化環境變數--common/env_flash.c或common/env_nand.c*/       init_baudrate,      /*初始化傳輸速率--lib_arm/board.c */       serial_init,            /*串口初始化-- drivers/serial/serial_s3c24x0.c */       console_init_f,    /*控制通訊台初始化階段1-- common/console.c */       display_banner,   /*列印U-Boot版本、編譯的時間--gedit lib_arm/board.c */       dram_init,            /*配置可用的RAM-- board/samsung/mini2440/mini2440.c */       display_dram_config,  /*顯示RAM大小--lib_arm/board.c */       NULL,};gd->bd->bi_arch_number = MACH_TYPE_MINI2440; /* 核心啟動參數地址 */gd->bd->bi_boot_params= 0x30000100;   
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;} 分析完上述的資料結構,下面來分析start_armboot函數:
void start_armboot (void){       init_fnc_t **init_fnc_ptr;       char *s;       … …       /* 計算全域資料結構的地址gd */       gd = (gd_t*)(_armboot_start - CONFIG_SYS_MALLOC_LEN - sizeof(gd_t));       … …       memset ((void*)gd, 0, sizeof (gd_t));       gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));       memset (gd->bd, 0, sizeof (bd_t));       gd->flags |= GD_FLG_RELOC;        monitor_flash_len = _bss_start - _armboot_start; /* 逐個調用init_sequence數組中的初始化函數  */       for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {              if ((*init_fnc_ptr)() != 0) {                     hang ();              }       } /* armboot_start 在cpu/arm920t/start.S 中被初始化為u-boot.lds串連指令碼中的_start */       mem_malloc_init (_armboot_start - CONFIG_SYS_MALLOC_LEN,                     CONFIG_SYS_MALLOC_LEN); /* NOR Flash初始化 */#ifndef CONFIG_SYS_NO_FLASH       /* configure available FLASH banks */       display_flash_config (flash_init ());#endif /* CONFIG_SYS_NO_FLASH */        … …/* NAND Flash 初始化*/#if defined(CONFIG_CMD_NAND)       puts ("NAND:  ");       nand_init();         /* go init the NAND */#endif       … …       /*配置環境變數,重新置放 */       env_relocate ();       … …       /* 從環境變數中擷取IP地址 */       gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr");       stdio_init (); /* get the devices list going. */       jumptable_init ();       … …       console_init_r (); /* fully init console as a device */       … …       /* enable exceptions */       enable_interrupts (); #ifdef CONFIG_USB_DEVICE       usb_init_slave();#endif        /* Initialize from environment */       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       … …       main_loop函數在common/main.c中定義。一般情況下,進入main_loop函數若干秒內沒有啟動Linux過程        U-Boot使用標記列表(tagged list)的方式向Linux傳遞參數。標記的資料結構式是tag,在U-Boot原始碼目錄include/asm-arm/setup.h中定義如下:struct tag_header {       u32 size;       /* 表示tag資料結構的聯合u實質存放的資料的大小*/       u32 tag;        /* 表示標記的類型 */}; struct tag {       struct tag_header hdr;       union {              struct tag_core           core;              struct tag_mem32      mem;              struct tag_videotext   videotext;              struct tag_ramdisk     ramdisk;              struct tag_initrd  initrd;              struct tag_serialnr       serialnr;              struct tag_revision      revision;              struct tag_videolfb     videolfb;              struct tag_cmdline     cmdline;               /*               * Acorn specific               */              struct tag_acorn  acorn;              /*               * DC21285 specific               */              struct tag_memclk      memclk;       } u;};       U-Boot使用命令bootm來啟動已經載入到記憶體中的核心。而bootm命令實際上調用的是do_bootm函數。對於Linux核心,do_bootm函數會調用do_bootm_linux函數來設定標記列表和啟動核心。do_bootm_linux函數在lib_arm/bootm.c 中定義如下:   int do_bootm_linux(int flag, int argc, char *argv[], bootm_headers_t *images)   {       bd_t       *bd = gd->bd;       int   machid = bd->bi_arch_number;       void       (*theKernel)(int zero, int arch, uint params);      #ifdef CONFIG_CMDLINE_TAG       char *commandline = getenv ("bootargs");    /* U-Boot環境變數bootargs */   #endif       … …       theKernel = (void (*)(int, int, uint))images->ep;  /* 擷取核心入口地址 */       … …}       標記列表必須以ATAG_CORE開始,setup_start_tag函數在核心的參數的開始地址設定了一個ATAG_CORE標記。           (4)    使資料Cache與指令Cache失效              由於U-Boot啟動以來就一直工作在SVC模式,因此CPU的工作模式就無需設定了。do_bootm_linux中:64       void       (*theKernel)(int zero, int arch, uint params);… …73       theKernel = (void (*)(int, int, uint))images->ep;… …128      theKernel (0, machid, bd->bi_boot_params);       第73行代碼將核心的入口地址“images->ep”強制類型轉換為函數指標。根據ATPCS規則,函數的參數個數不超過4個時,使用r0~r3這4個寄存器來傳遞參數。因此第128行的函數調用則會將0放入,r0,機器碼machid放入r1,核心參數地址bd->bi_boot_params放入r2,從而完成了寄存器的設定,最後轉到核心的入口地址。
  到這裡,U-Boot的工作就結束了,系統跳轉到Linux核心代碼執行。
 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.