深入剖析barebox(U-BOOT-II)在i.MX27上的移植

莱得科技   2011-10-31 12:46 楼主

深入剖析barebox(U-BOOT-II)i.MX27上的移植

 

                              

Barebox是一个在uboot的基础上发展起来的一个新的功能强大的bootloader,它有着非常直观的设备模型和友好的编程接口,使用方便、功能强大。以下使用平台为成都莱得科技的i.MX27开发板上,平台稳定并应用到数十个客户的产品中。以下是详细的移植步骤。

文章版权属于成都莱得科技有限责任公司所有,转载请注明出处。

网址:www.nidetech.com,联系电话:18080873876,邮箱:nidetech@163.com,技术交流QQ1460879610

 

1 barebox的特色和亮点

1.1   POSIX File API

barebox使用open/close/read/write/lseek 函数,并且提供了设备文件的模型,可以通过使用像linux下被人熟知的这些API函数来操作设备。

1.2   Shell

提供了标准的shell 命令,例如 cd/ls/cat/echo …等等

1.3   Envionment Filesystem

barebox可以像操作文件一样的将环境变量保存在一个flash中,并且可以加载到ram中来。环境变量被模拟成了一个文件,可以通过barebox下简单的文本编辑器来编辑和保存环境变量文件。

1.4   Filesystem Support

barebox启动后,环境变量被挂载到/env/目录下,所有的设备被挂载到/dev目录下,我们可以通过标准的API函数来操作这些设备文件,就像在linux下一样。并且还可以通过mount指令挂载其他文件系统

1.5   Driver Model(borrowed from Linux)

提供了像linux一样的驱动开发模型,在板级支持文件中定义所有的设备名,然后在驱动中通过相同的名字负责探测和管理这些设备。

1.6   Clocksource

提供了在linux下熟知的clocksource函数,来用统一的管理时钟,例如clk_get,clk_enable

1.7   Sandbox

你可以通过将barebox选择成为sandbox来方便的进行barebox开发,他会将barebox编译成为在linux用户空间上的一个POSIX应用程序。方便开发和调试。

1.8   Device Parameters

barebox提供了一个参数模型,每个设备都可以指定他们自己的参数,可以通过<devid>.<param> = ….”这样的方式来修改设备的参数,例如可以通过eth0.ipaddr=xxx.xxx.xxx.xxx来修改第一块网卡的ip地址。

1.9   Getopt

barebox实现了一个简单的getopt,提供了一种比通过位置来传递参数更方便的模式。

1.10                    Integrated Editor

内部集成了一个文本编辑器

1.11                    集成了许多方便调试的命令和功能

l  md –s /dev/mem  [寄存器的物理地址]  可以查看任意寄存器和物理地址空间中的值。

l  mw 可以设置某个寄存器或者物理内存地址的值。

l  gpio_set_value/gpio_get_value 可以设置某个gpio的输出。

1.12                    支持网络更新自身、内核、文件系统、环境变量。

       1. 可以支持通过串口或者网络更新barebox自身

       2. 可以通过网络下载zImage,uImage,raw格式的内核

       3. 支持jffs2,ubifs等格式的文件系统映像

2 barebox的目录结构和移植相关文件

arch/*/                  -> contains architecture specific parts

arch/*/mach-*/           -> SoC specific code

drivers/serial           -> drivers

drivers/net

drivers/...

include/asm-*            -> architecture specific includes

include/asm-*/arch-*     -> SoC specific includes

fs/                      -> filesystem support and filesystem drivers

lib/                     -> generic library functions (getopt, readline and the

                            like)

common/                  -> common stuff

commands/                -> many things previously in common/cmd_*, one command

                           per file

net/                     -> Networking stuff

scripts/                 -> Kconfig system

Documentation/           -> Parts of the documentation, also doxygen

 

移植相关的文件:按启动顺序

arch/arm/lib/barebox.lds.S ->  针对arm体系下,Barebox的链接脚本,定义了barebox映像在内存中的布局

arch/arm/cpu/start.c  -> arm体系,barebox针对arm体系的入口函数,负责arm体系的初始化

arch/arm/boards/xxx/ lowlevel_init.[c/S] -> 针对某个具体ARM芯片的低层初始化文件,板级初始化文件

common/startup.c  -> 与体系架构无关的 barebox 启动代码,加载各个驱动,并且启动bareboxshell,等待用户输入。

3 barebox的详细启动流程

l  i.MX27冷启动后,nand flash控制器中有2K SRAM会被用作boot ram,根据硬件设计如果支持从nandflash启动的话,mx27会将NAND FLASH中的前2K的数据自动拷贝到NAND FLASH 控制器中的 RAM中,并且自动跳转到该2K RAM的开始地址 0xD8000000开始执行。因为我们的barebox是被atk烧写工具烧写到nandflash0地址处,所以barebox的前2K代码会被拷贝到0xD8000000barebox从而获得被调用的机会。

l  那么barebox中首先得以执行的函数或者文件在什么地方啦?这就要查看barebox的链接脚本了,对于每种cpu架构,barebox在其架构名所在的文件夹中都有一个针对该架构的链接文件,对于arm来说,就是arch/arm/lib/barebox.lds.S文件,我们来详细分析一下该文件,弄清楚barebox在运行时候的映像分布情况

 

arch/arm/lib/barebox.lds.S

该头文件中定义了一些节的定义宏,方便barebox来定义输出节名

#include <asm-generic/barebox.lds.h>

 

OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")

OUTPUT_ARCH(arm)

ENTRY指示出了Barebox的入口点,也就是在barebox被执行的第一条指令的地方。

ENTRY(exception_vectors)

以下描述了barebox中各个代码和数据的分布情况

SECTIONS

{

定义了barebox最终需要重定位的地址,在我们的开发板中定义为0xa7f00000,也就是硬件上设计的SDRAM的最后1M地址处,这里说的地址都是物理地址,没有经过mmu转化的。

. = TEXT_BASE;

这个是对于支持内部启动的imx系列cpu才支持的flash header比如mx51/53等,对于mx27来说,这个宏是个空的,也就是没有定义,所以我们不需要去管

PRE_IMAGE  

代码段需要以4字节对齐,以便取指令

. = ALIGN(4);

定义了代码段,对于mx27来说代码段的地址等于TEXT_BASE

.text      :

{

        _stext = .;  将代码段和只读数据段的起始处 == TEXT_BASE

        _text = .;   和上面的_stext一样

        *(.text_entry*)  所有的代码段

#ifdef CONFIG_ARCH_EP93XX

        /* the EP93xx expects to find the pattern 'CRUS' at 0x1000 */

  . = 0x1000;

  LONG(0x53555243) /* 'CRUS' */

#endif

        *(.text_bare_init*)

        *(.text*)

}

 

. = ALIGN(4);

.rodata : { *(.rodata*) }

 

#ifdef CONFIG_ARM_UNWIND

/*

 * Stack unwinding tables

 */

. = ALIGN(8);

.ARM.unwind_idx : {

        __start_unwind_idx = .;

        *(.ARM.exidx*)

        __stop_unwind_idx = .;

}

.ARM.unwind_tab : {

        __start_unwind_tab = .;

        *(.ARM.extab*)

        __stop_unwind_tab = .;

}

#endif

_etext = .;                   /* End of text and rodata section */

 

. = ALIGN(4);

.data : { *(.data*) }

 

. = ALIGN(4);

.got : { *(.got*) }

 

. = .;

__barebox_cmd_start = .;

.barebox_cmd : { BAREBOX_CMDS }

__barebox_cmd_end = .;

 

__barebox_initcalls_start = .;

.barebox_initcalls : { INITCALLS }

__barebox_initcalls_end = .;

 

__usymtab_start = .;

__usymtab : { BAREBOX_SYMS }

__usymtab_end = .;

 

. = ALIGN(4);

__bss_start = .;

.bss : { *(.bss*) }

__bss_stop = .;

_end = .;

_barebox_image_size = __bss_start - _text;

}

 从上面的链接脚本可以看出barebox首先被执行的是 exception_vectors

该函数在 arch/arm/cpu/start.c

void __naked __section(.text_entry) exception_vectors(void)

{

       __asm__ __volatile__ (

              "b reset\n"                          /* reset */

#ifdef CONFIG_ARM_EXCEPTIONS

              "ldr pc, =undefined_instruction\n"    /* undefined instruction */

              "ldr pc, =software_interrupt\n"         /* software interrupt (SWI) */

              "ldr pc, =prefetch_abort\n"        /* prefetch abort */

              "ldr pc, =data_abort\n"                     /* data abort */

              "1: bne 1b\n"                      /* (reserved) */

              "ldr pc, =irq\n"                    /* irq (interrupt) */

              "ldr pc, =fiq\n"                    /* fiq (fast interrupt) */

#else

              "1: bne 1b\n"                      /* undefined instruction */

              "1: bne 1b\n"                      /* software interrupt (SWI) */

              "1: bne 1b\n"                      /* prefetch abort */

              "1: bne 1b\n"                      /* data abort */

              "1: bne 1b\n"                      /* (reserved) */

              "1: bne 1b\n"                      /* irq (interrupt) */

              "1: bne 1b\n"                      /* fiq (fast interrupt) */

#endif

              ".word 0x65726162\n"                     /* 'bare' */

              ".word 0x00786f62\n"               /* 'box' */

              ".word _text\n"                          /* text base. If copied there,

                                                  * barebox can skip relocation

                                                  */

              ".word _barebox_image_size\n"              /* image size to copy */

       );

}

由此可见第一条指令就是b reset, barebox然后跳转到了reset函数

该函数在 arch/arm/cpu/start.c

      

       board_init_lowlevel:

 

       mov r10, lr

       /* ahb lite ip interface */

       writel(0x20040304, AIPI1_PSR0)

       writel(0xDFFBFCFB, AIPI1_PSR1)

       writel(0x00000000, AIPI2_PSR0)

       writel(0xFFFFFFFF, AIPI2_PSR1)

 

       /* skip sdram initialization if we run from ram [0xa0000000--0xc0000000 is ram space]*/

       cmp pc, #0xa0000000

       bls   1f

       cmp pc, #0xc0000000

       bhi  1f

 

       mov pc,r10

 

1:

       writel(IMX_PLL_PD(0) |

               IMX_PLL_MFD(51) |

               IMX_PLL_MFI(7) |

               IMX_PLL_MFN(35), MPCTL0) /* 399 MHz */

 

       writel(IMX_PLL_PD(1) |

               IMX_PLL_MFD(12) |

               IMX_PLL_MFI(9) |

               IMX_PLL_MFN(3), SPCTL0) /* SPLL = 2 * 26 * 4.61538 MHz = 240 MHz */

 

       writel(CSCR_MPLL_RESTART | CSCR_SPLL_RESTART | CSCR_ARM_SRC_MPLL |

              CSCR_MCU_SEL | CSCR_SP_SEL | CSCR_FPM_EN | CSCR_MPEN |

              CSCR_SPEN | CSCR_ARM_DIV(0) | CSCR_AHB_DIV(1) | CSCR_USB_DIV(3) |

              CSCR_SD_CNT(3) | CSCR_SSI2_SEL | CSCR_SSI1_SEL | CSCR_H264_SEL |

              CSCR_MSHC_SEL, CSCR)

 

       sdram_init

#ifdef CONFIG_NAND_IMX_BOOT

       ldr   sp, =0xa0f00000         /* Setup a temporary stack in SDRAM [ram start+15M offset] */

 

       ldr   r0, =IMX_NFC_BASE        /* start of NFC SRAM                */

       ldr   r2, =IMX_NFC_BASE + 0x1000       /* end of NFC SRAM                  */

 

       /* skip NAND boot if not running from NFC space */

       cmp pc, r0

       bls   ret                 #if(pc < r0) goto ret

       cmp pc, r2           

       bhi  ret                 ##if(pc > r2) goto ret

 

       /* Move ourselves out of NFC SRAM */

       ldr   r1, =TEXT_BASE  #0xa7f0_0000

 

copy_loop:

       ldmia      r0!, {r3-r9}            /* copy from source address [r0]    */

       stmia      r1!, {r3-r9}            /* copy to   target address [r1]    */

       cmp r0, r2                    /* until source end addreee [r2]    */

       ble  copy_loop

 

       ldr   pc, =1f                 /* Jump to SDRAM                    */

1:

       bl    nand_boot           /* Load barebox from NAND Flash      */

 

       ldr   r1, =IMX_NFC_BASE - TEXT_BASE

       sub r10, r10, r1          /* adjust return address from NFC SRAM */

                                   /* to SDRAM                            */

 

#endif /* CONFIG_NAND_IMX_BOOT */

 

ret:

       mov pc,r10

 

Reset函数流程如下

l  ARM进入svc32 mode 因为arm芯片内部有许多寄存器都只能在SVC32mode下才能访问,否则会产生abort异常,或则读取不到数据。比如mx27内部的AIPI模块

l  禁用MMUcache  必须采用物理地址访问而且不能进行cache

l  调用board_init_lowlevel函数进行板级初始化

l  调用board_init_lowlevel_return进行代码的重定位

board_init_lowlevel函数在 arch/arm/boards/你的电路板目录下面/lowlevel.c文件中,流程如下

l  保护lr地址到r10中,以便返回

l  配置AIPI接口,设置外设模块总线宽度

l  判断当前程序是否在RAM中执行,如果已经在RAM中则直接返回,因为有可能barebox是通过tftp下载到ram中,然后再跳转执行的,因为已经完成了后面的初始化了,所以直接返回。

l  设置MPLLSPLL时钟 分别为400M240M

l  设置时钟源寄存器,从而设置各个外设的时钟

l  初始化SDRAM

l  判断自己是否在NANDFLASH的内部RAM中,如果是则将自己拷贝2KSDRAM中的TEXT_BASE

l  拷贝完毕后,跳转到SDRAM中接着从跳转前的偏移位置处开始执行,也就是bl    nand_boot,至于为什么要这样做,是因为接下来马上要从NANDFLASH中拷贝剩余的barebox代码到SDRAM中,而一旦拷贝就需要用到NAND FLASH控制器中的SRAM也就是boot ram,而拷贝前我们代码正是在这段ram中,所以我们要把这段ram让出来。

l  现在我们的pc指针已经在sdram中了,接着调用nand_boot函数将nandflash中的barebox完整的拷贝到sdramTEXT_BASE处。拷贝的大小是256K

l  接着计算出NANDFLASH控制器中RAM的起始地址和SDRAMTEXT_BASE的偏移地址,然后将第一步保存的lr地址减去计算出来的偏移地址就得到了,board_init_lowlevel函数经过重新拷贝到sdram中后应该返回的地址。然后跳转到该地址去

l  跳转到board_init_lowlevel_return 继续执行

l  那么你可能会问了,既然mx27在冷启动的时候只能复制nandflash中的前2k代码出来,那么我们怎么才能保证exception_vectorsreset函数,还有board_init_lowlevel函数,以及nand_boot函数在前面的2K代码内。这还得看链接文件

.text      :

{

        _stext = .;

        _text = .;

        *(.text_entry*)

        *(.text_bare_init*)

        *(.text*)

}

              看看几个函数的申明

       void __naked __section(.text_entry) exception_vectors(void)

说明exception_vectors*(.text_entry*)节中

              void __naked __bare_init reset(void)

              void __bare_init nand_boot(void)

                     .section ".text_bare_init","ax”

.globl board_init_lowlevel

board_init_lowlevel:

       #define __bare_init          __section(.text_bare_init.text)

说明这三个函数在*(.text_bare_init*)节中,这样就保证了这4个关键函数在最前面的2K,不会将他们链接到其他地方去。

 

l  board_init_lowlevel_return函数主要是继续地址重定位,当barebox的代码定位到TEXT_BASE处,为什么前面的board_init_lowlevel中以及进行了重定位了,这里还需要重定位啦?这是因为board_init_lowlevel中的重定位操作是可以配置的,也就是某些平台可能没有启用NAND FLASH启动方式,所以这里需要再检查一下是否以及重定位了,如果没有在进行一次重定位操作,然后跳转到start_barebox中,完成了最底层的初始化加载操作。

l  start_barebox 函数在common/startup.c中,主要按照预先设置的顺序调用核心模块以及负责加载驱动等等,所有在这里被调用的函数都属于barebox.lds.S

__barebox_initcalls_start = .;

.barebox_initcalls : { INITCALLS }

__barebox_initcalls_end = .;

 

#define INITCALLS                   \

KEEP(*(.initcall.0))                    \

KEEP(*(.initcall.1))                    \

KEEP(*(.initcall.2))                    \

KEEP(*(.initcall.3))                    \

KEEP(*(.initcall.4))                    \

KEEP(*(.initcall.5))                    \

KEEP(*(.initcall.6))                    \

KEEP(*(.initcall.7))                    \

KEEP(*(.initcall.8))                    \

KEEP(*(.initcall.9))                    \

KEEP(*(.initcall.10))                  \

KEEP(*(.initcall.11))

 

#define pure_initcall(fn)             __define_initcall("0",fn,0)

 

#define core_initcall(fn)             __define_initcall("1",fn,1)

#define postcore_initcall(fn)             __define_initcall("2",fn,2)

#define console_initcall(fn)        __define_initcall("3",fn,3)

#define postconsole_initcall(fn) __define_initcall("4",fn,4)

#define mem_initcall(fn)            __define_initcall("5",fn,5)

#define mmu_initcall(fn)            __define_initcall("6",fn,6)

#define postmmu_initcall(fn)             __define_initcall("7",fn,7)

#define coredevice_initcall(fn)          __define_initcall("8",fn,8)

#define fs_initcall(fn)                 __define_initcall("9",fn,9)

#define device_initcall(fn)          __define_initcall("10",fn,10)

#define late_initcall(fn)              __define_initcall("11",fn,11)

 

linux驱动一样的处理方式。所以我们只要熟知linux内核,理解起barebox就很容易了,他里面很多东西都是借鉴的linux内核。

从上面可以看出core_initcall所定义的函数调用的优先级别最高,比如

core_initcall(clocksource_init); clocksource_init函数是delay系列函数实现的基础,而该函数是非常基础的函数,所以我们要将clocksource_init定义为高优先级函数。至于我们的设备驱动则是device_initcall(fn),可以看出他的优先级是比较低的了,可以看出设备驱动的加载必须是再barebox的核心组件都初始化完毕后再加载的。

core_initcall(arm_mem_malloc_init);负责mallocfree函数的实现

postcore_initcall(getc_buffer_flush);

postcore_initcall(env_push_context);

postcore_initcall(init_cwd);

postcore_initcall(net_init);

postcore_initcall(hist_init);

在完成init系列函数的初始化后,barebox开始从flash中加载环境变量,并且将环境变量挂载到/env目录下,包括

Env/config 配置文件

Env/bin/init 根据配置文件中的内容执行一些初始化

Env/bin/boot  加载内核的一些列脚本

最后进入shell死循环,等待用户的输入。如果用户不输入,超时时间到达后,自动执行env/bin/boot脚本,完成内核的加载。Barebox至此启动完成。

4 移植步骤

l  从官网下载并且解压barebox

u  cd ~

l  git clone git://git.pengutronix.de/git/barebox.git barebox

l  cd barebox

l  arch/arm/boards下建立硬件电路板对应的工程文件夹,比如我们的电路板叫nidetech-mx27

u  mkdir –p arch/arm/boards/nidetech-mx27

l  复制和你开发板最相近的板级支持工程文件到你的工程目录,我们的开发板跟pca100比较相近,所以我选择这个工程作为模板

u  cd arch/arm/boards/nidetech-mx27

u  cp arch/arm/boards/phycard-i.MX27/*  ./  -a

u  复制默认的环境变量文件夹到工程目录

u  cp defaultenv/config arch/arm/boards/nidetech-mx27/env

l  修改KconfigMakefile以支持新的电路板nidetech-mx27

根据cpu的平台选择相应的Kconfig来修改,例如nidetech-mx27是采用的是freescaleimx系列cpu,那么我们就要修改如下文件

Arch/arm/mach-imx/Kconfig

在其中为自己的电路板添加一项

因为我们的cpuimx27,所以找到红色处,再其后面再添加一项基于mx27的电路板

if ARCH_IMX27

choice

prompt "i.MX27 Board Type"

config MACH_NIDETECH_MX27

     bool "nidetech-i.MX27"

     select MACH_HAS_LOWLEVEL_INIT

     help

       Say Y here if you are using Phytec's nidetech-i.MX27 equipped

       with a Freescale i.MX27 Processor

 

a.定义boardinfoboardinfo就是barebox启动后打印出来的板子的名称信息

config BOARDINFO

     default "Nidetech i.MX27" if MACH_NIDETECH_MX27

b. 定义默认的代码段基地址 TEXT_BASE

config ARCH_TEXT_BASE

     default 0xa7f00000 if MACH_PCA100

c.定义自己的mach-type

arch/arm/tools/mach-types文件中添加自己的mach-type

d.修改makefile文件,支持新工程的编译

arch/arm/Makefile中添加

Board-$(CONFIG_MACH_NIDETECH_MX27) := nidetech-mx27(工程目录名)

l  配置并编译,barebox支持menuconfig,通过可视化菜单来选择

电路板的相关配置

make ARCH=arm CROSS_COMPILE=arm-v5te-linux-gnueabi- menuconfig

l  设置System Type

 

 在这里可以选择你自己的电路板类型nidetech-i.MX27

  如果需要支持从NAND FLASH中启动bootloader的话,必须选择support starting barebox from nand

l  General Setting

 

如果你想支持tab键自动补齐命令和文件名的话,必须选择Enable command line editing enable auto completion

选择activate first console on startup

如果想使用单独的默认的环境变量的话,选择compile in default environment,并且制定默认环境的路径在你新建板子的工程目录下。

make ARCH=arm CROSS_COMPILE=arm-v5te-linux-gnueabi- 来编译。编译成功的话,可以在根目录下发现barebox.bin文件。

5 运行

1.    atktool工具将barebox.bin烧入到nandflash的地址0x0

2.    barebox的环境变量介绍

barebox的环境变量保持在/env/config文件中。我们可以用edit命令来编辑该文件

edit env/config

ctrl+d保存修改并退出,ctrl+c取消修改并退出

eth0.ipaddr=192.168.50.127  :设备ip地址

eth0.netmask=255.255.255.0  :设备的子网掩码

eth0.gateway=192.168.50.1   :设备的网关

eth0.serverip=192.168.50.87    服务器ip,通常是内核或者nfs服务器的地址

eth0.ethaddr=00:11:22:33:44:aa  :设备的mac地址

ip = none :不在内核中设置ip地址  dhcp通过dhcp来获取设备ip

# can be either 'nfs', 'tftp', 'nor' or 'nand'

kernel_loc=nand :内核的加载路径,可以支持tftp加载serverip的上的内核,或者从nand中启动

# can be either 'net', 'nor', 'nand' or 'initrd'

rootfs_loc=nand rootfs的加载路径,可以支持从nfs启动,或则从本机的nand启动

支持zImageuImage,raw,raw_lzo类型的内核

# The image type of the kernel. Can be uimage, zimage, raw, or raw_lzo

#kernelimage_type=zimage

#kernelimage=zImage-$machine

kernelimage_type=uimage

kernelimage=uImage-$machine

#kernelimage_type=raw

#kernelimage=Image-$machine

#kernelimage_type=raw_lzo

#kernelimage=Image-$machine.lzo

 

修改后,如果需要保存到flash中,则必须输入save命令即可。

 

如何更新barebox,内核,以及文件系统

Update –t barebox –d nand

通过tftp从服务器上取得barebox.bin文件,并且重新写入到barebox所在的分区。

Update –t kernel –d nand :  下载并烧写内核

Update –t rootfs –d nand :下载并烧写文件系统

6 Barebox链接技术详细分析

Barebox的链接文件

arch\arm\mach-imx\include\mach\barebox.lds.h 定义了内部启动模式下的映像头部信息

#ifdef CONFIG_ARCH_IMX_INTERNAL_BOOT

#define PRE_IMAGE \

.pre_image : {                                  \

        KEEP(*(.flash_header_start*))         \

        . = 0x100;                          \

        KEEP(*(.flash_header_0x0100*))           \

        KEEP(*(.dcd_entry_0x0100*))         \

        KEEP(*(.image_len_0x0100*))         \

        . = 0x400;                          \

        KEEP(*(.flash_header_0x0400*))           \

        KEEP(*(.dcd_entry_0x0400*))         \

        KEEP(*(.image_len_0x0400*))         \

        . = 0x1000;                        \

        KEEP(*(.flash_header_0x1000*))           \

        KEEP(*(.dcd_entry_0x1000*))         \

        KEEP(*(.image_len_0x1000*))         \

        . = 0x2000;                        \

}

#endif

include\asm-generic\ barebox.lds 中定义了链接节定义宏,可以方便的定义一些预定义节

被定义在这些节中的函数在common/startup.c中的startup函数中被调用,调用的顺序是从小到大也就是.initcall.0.initcall.11这样可以保证优先级最高的函数最先被调用

#define INITCALLS                   \

KEEP(*(.initcall.0))                    \

KEEP(*(.initcall.1))                    \

KEEP(*(.initcall.2))                    \

KEEP(*(.initcall.3))                    \

KEEP(*(.initcall.4))                    \

KEEP(*(.initcall.5))                    \

KEEP(*(.initcall.6))                    \

KEEP(*(.initcall.7))                    \

KEEP(*(.initcall.8))                    \

KEEP(*(.initcall.9))                    \

KEEP(*(.initcall.10))                  \

KEEP(*(.initcall.11))

这里定义了barebox的命令函数节,barebox中所有的命令函数被定义到了这个节中

#define BAREBOX_CMDS KEEP(*(SORT_BY_NAME(.barebox_cmd*)))

#define BAREBOX_SYMS  KEEP(*(__usymtab))

Barebox的时钟源分析

Arch/arm/mach-imx/clocksource.c

通用定时器1被用作时钟源

static int clocksource_init (void)

{

int i;

uint32_t val;

/* setup GP Timer 1 */   软复位定时器

writel(TCTL_SWR, timer_base + GPT_TCTL);

 

#ifdef CONFIG_ARCH_IMX21

PCCR1 |= PCCR1_GPT1_EN;

#endif

#ifdef CONFIG_ARCH_IMX27

PCCR0 |= PCCR0_GPT1_EN;         启用gpt1ipg时钟

PCCR1 |= PCCR1_PERCLK1_EN;     启用外设1时钟

#endif

#ifdef CONFIG_ARCH_IMX25

writel(readl(IMX_CCM_BASE + CCM_CGCR1) | (1 << 19),

        IMX_CCM_BASE + CCM_CGCR1);

#endif

for (i = 0; i < 100; i++)

        writel(0, timer_base + GPT_TCTL); /* We have no udelay by now */

writel(0, timer_base + GPT_TPRER);

val = readl(timer_base + GPT_TCTL);

val |= TCTL_FRR | (1 << TCTL_CLKSOURCE) | TCTL_TEN; /* Freerun Mode, PERCLK1 input */

writel(val, timer_base + GPT_TCTL);

将时钟频率转化成倍数以便以后转化

cs.mult = clocksource_hz2mult(imx_get_gptclk(), cs.shift);

init_clock(&cs);

clock_register_client(&imx_clock_notifier);

return 0;

}

core_initcall(clocksource_init);  该函数在以优先级3startup.c中被调用

 

1.barebox一般不用clk_getclk_enable来获取和设置时钟,而是直接在板级初始化文件中来初始化外设的时钟,或者在驱动中启动外设的时钟,为什么不用标准的clk_xx函数来管理啊?

 

MPLLSPLL的输入是内部FPM或者标准外部OSC经过倍频后产生的,每个都可以选择其时钟源,要么为FPM要么为extosc[26M/27M]

MPLL : 主要用于产生系统时钟和cpu时钟,以及AHBIPG时钟,以及大部分芯片内部的外设的divider的时钟源,包括PER1-PER4divider时钟,以及NFCdivider的时钟,其余的如i2crtcgpiowdgkpp等都采用ipg时钟来工作。

SPLL :主要用于产生特殊外设的时钟,比如作为USB/SSI1/SSI2/H264/MSHDdivider的输入时钟信号

AHB时钟:由mpll时钟分频后产生[ahbdiv 1-4 分之 mpll]

ARM时钟:由mpll时钟经armdiv分频后产生,其中还可以选择是直接对mpll分频还是mpll*2/3后再分频。

IPG时钟 等与AHB时钟分频后获得,所有外设都有AHB时钟,而且外设的APB时钟都是一样频率,主要用这个时钟来进行ARM和外设直接的通信,比如读取寄存器,传输数据等等,以及外设的工作时钟。还有一个时钟是某些外设特有的时钟,比如定时器需要一个计数的时钟,SSI需要一个传输的时钟,PER1—PER4需要的时钟,SSI1/2H264,的时钟,

一般来说低速外设是IPG时钟,高速是AHB时钟,要看设备是挂载什么总线上的,是IP还是AHB。某些外设出了工作时钟外还需要特定的外设时钟或则像素时钟等等。所以一个外设至少需要1个时钟,要么是IPG要么是AHB,其余的时钟需要根据设备的具体需要而定。

其中SSI1DIVSSI2DIVMSHCDIVH264DIV的时钟输入可以编程选择MPLL或者SPLL

每个外设有1个或者多个时钟需要启用,有些是IPG时钟,有些是AHB时钟,还有其他外设时钟等等。是ipg/ahb/perclk的组合。其中ipg是必须的,是arm和外设进行寄存器读写的时钟,ahb是高速外设传输的时钟,外设时钟是低速外设的时钟。

GPT 使用PERCLK1作为其计数器工作的时钟,用IPG时钟作为和ARM通信的时钟进行寄存器读写操作。还有模块工作的时钟,没有这个时钟模块就没有办法工作。或则这样来认为。并且arm核无法和module通信。

FCLK 就是cpu工作的频率

HCLK 就是AHB总线的工作频率  AHB连接高速的外设,也是系统时钟 AHB CLK

PCLK 就是 APB 总线工作频率 APB总线连接低速的外设 IPGCLK

rtc/wdg/gpio/dmac/rtic/i2c/fec/kpp

 

回复评论

暂无评论,赶紧抢沙发吧
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复