深入剖析barebox(U-BOOT-II)在i.MX27上的移植
Barebox是一个在uboot的基础上发展起来的一个新的功能强大的bootloader,它有着非常直观的设备模型和友好的编程接口,使用方便、功能强大。以下使用平台为成都莱得科技的i.MX27开发板上,平台稳定并应用到数十个客户的产品中。以下是详细的移植步骤。
文章版权属于成都莱得科技有限责任公司所有,转载请注明出处。
网址:www.nidetech.com,联系电话:18080873876,邮箱:nidetech@163.com,技术交流QQ:1460879610
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 启动代码,加载各个驱动,并且启动barebox的shell,等待用户输入。
3 、barebox的详细启动流程
l i.MX27冷启动后,nand flash控制器中有2K 的SRAM会被用作boot ram,根据硬件设计如果支持从nandflash启动的话,mx27会将NAND FLASH中的前2K的数据自动拷贝到NAND FLASH 控制器中的 RAM中,并且自动跳转到该2K RAM的开始地址 0xD8000000开始执行。因为我们的barebox是被atk烧写工具烧写到nandflash的0地址处,所以barebox的前2K代码会被拷贝到0xD8000000,barebox从而获得被调用的机会。
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 禁用MMU和cache 必须采用物理地址访问而且不能进行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 设置MPLL和SPLL时钟 分别为400M和240M
l 设置时钟源寄存器,从而设置各个外设的时钟
l 初始化SDRAM
l 判断自己是否在NANDFLASH的内部RAM中,如果是则将自己拷贝2K到SDRAM中的TEXT_BASE处
l 拷贝完毕后,跳转到SDRAM中接着从跳转前的偏移位置处开始执行,也就是bl nand_boot,至于为什么要这样做,是因为接下来马上要从NANDFLASH中拷贝剩余的barebox代码到SDRAM中,而一旦拷贝就需要用到NAND FLASH控制器中的SRAM也就是boot ram,而拷贝前我们代码正是在这段ram中,所以我们要把这段ram让出来。
l 现在我们的pc指针已经在sdram中了,接着调用nand_boot函数将nandflash中的barebox完整的拷贝到sdram的TEXT_BASE处。拷贝的大小是256K。
l 接着计算出NANDFLASH控制器中RAM的起始地址和SDRAM的TEXT_BASE的偏移地址,然后将第一步保存的lr地址减去计算出来的偏移地址就得到了,board_init_lowlevel函数经过重新拷贝到sdram中后应该返回的地址。然后跳转到该地址去
l 跳转到board_init_lowlevel_return 继续执行
l 那么你可能会问了,既然mx27在冷启动的时候只能复制nandflash中的前2k代码出来,那么我们怎么才能保证exception_vectors和reset函数,还有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);负责malloc、free函数的实现
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 修改Kconfig和Makefile以支持新的电路板nidetech-mx27
根据cpu的平台选择相应的Kconfig来修改,例如nidetech-mx27是采用的是freescale的imx系列cpu,那么我们就要修改如下文件
Arch/arm/mach-imx/Kconfig
在其中为自己的电路板添加一项
因为我们的cpu是imx27,所以找到红色处,再其后面再添加一项基于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.定义boardinfo,boardinfo就是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启动
支持zImage和uImage,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; 启用gpt1的ipg时钟
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_get和clk_enable来获取和设置时钟,而是直接在板级初始化文件中来初始化外设的时钟,或者在驱动中启动外设的时钟,为什么不用标准的clk_xx函数来管理啊?
MPLL和SPLL的输入是内部FPM或者标准外部OSC经过倍频后产生的,每个都可以选择其时钟源,要么为FPM要么为extosc[26M/27M]
MPLL : 主要用于产生系统时钟和cpu时钟,以及AHB和IPG时钟,以及大部分芯片内部的外设的divider的时钟源,包括PER1-PER4的divider时钟,以及NFC的divider的时钟,其余的如i2c、rtc、gpio、wdg、kpp等都采用ipg时钟来工作。
SPLL :主要用于产生特殊外设的时钟,比如作为USB/SSI1/SSI2/H264/MSHD的divider的输入时钟信号
AHB时钟:由mpll时钟分频后产生[ahbdiv 1-4 分之 mpll]
ARM时钟:由mpll时钟经armdiv分频后产生,其中还可以选择是直接对mpll分频还是mpll*2/3后再分频。
IPG时钟 : 等与AHB时钟分频后获得,所有外设都有AHB时钟,而且外设的APB时钟都是一样频率,主要用这个时钟来进行ARM和外设直接的通信,比如读取寄存器,传输数据等等,以及外设的工作时钟。还有一个时钟是某些外设特有的时钟,比如定时器需要一个计数的时钟,SSI需要一个传输的时钟,PER1—PER4需要的时钟,SSI1/2,H264,的时钟,
一般来说低速外设是IPG时钟,高速是AHB时钟,要看设备是挂载什么总线上的,是IP还是AHB。某些外设出了工作时钟外还需要特定的外设时钟或则像素时钟等等。所以一个外设至少需要1个时钟,要么是IPG要么是AHB,其余的时钟需要根据设备的具体需要而定。
其中SSI1DIV、SSI2DIV、MSHCDIV、H264DIV的时钟输入可以编程选择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