在上一篇文章 U-Boot详细分析(2)——Exynos4412启动过程中可以看到,BL2程序流程图中有SET CLOCK’s这一步骤, 并且在U-Boot源码的 board/samsung/smdkc210/lowlevel_init_SCP.S的文件中也有着对时钟初始化的相关代码(因为BL2中已经初始化,所以不一定会执行)
这篇文章通过U-Boot中对时钟进行初始化的源码和三星提供的4412的datasheet来学习Exynos 4412的时钟体系结构和时钟的相关操作
在Exynos 4412的datasheet里第七章“时钟管理单元(Clock Management Unit)”详细介绍了4412的时钟体系结构
首先可以看一下Exynos 4412 SCP的总框图:
在4412的datasheet中可找到
The top-level clocks in Exynos 4412 SCP are:
- Clocks from clock pads, namely, XRTCXTI, XXTI, and XUSBXTI.
- Clocks from CMUs
- Clocks from USB PHY
- Clocks from HDMI_PHY
- Clocks from GPIO pads
从第一个"clocks from clock pads…"可以看到顶层的时钟包括着三个时钟引脚,分别是:
打开讯为提供的Exynos 4412的原理图,可以看到XUSBXTI接了24MHz的晶振,XXTI接地:
刚才可以看到XUSBXTI接了24MHz的晶振作为系统时钟源的输入,但是4412CPU的频率可以达到1.4GHz,所以肯定需要相应的部件把24MHz的频率提升到1.4GHz
PLL(锁相环)使用一个外部晶振作为输入,可以对外部晶振所产生的频率进行倍频或分频操作
4412一共有4个PLL:
在board/samsung/smdkc210/lowlevel_init_SCP.S的文件中,标号system_clock_init_scp中对应代码是时钟初始化的部分
首先执行:
push {lr}
ldr r0, =ELFIN_CLOCK_BASE @0x1003_0000
接下选择CMU_CPU的时钟源:
@ CMU_CPU MUX / DIV
ldr r1, =0x0
ldr r2, =CLK_SRC_CPU_OFFSET
str r1, [r0, r2]
ldr r2, =CLK_MUX_STAT_CPU_OFFSET
ldr r3, =0x01110001
bl wait_mux_state
MUX_APLL_SEL = 0 = FINPLL
MUX_CORE_SEL = 0 = MOUTAPLL
剩下两个MUX保持复位状态0
MPLL_USER_SEL_C = FINMPLL =1
HPM_SEL = MOUTAPLL = 1
CORE_SEL = MOUTAPLL =1
ARLL_SEL = FINPLL = 1
此寄存器值为0x0111_0001
wait_mux_state:
ldr r1, [r0, r2]
cmp r1, r3
bne wait_mux_state
mov pc, lr
随后设置了CMU_DMC的相关分频比
ldr r1, =CLK_DIV_DMC0_VAL
ldr r2, =CLK_DIV_DMC0_OFFSET
str r1, [r0, r2]
ldr r1, =CLK_DIV_DMC1_VAL
ldr r2, =CLK_DIV_DMC1_OFFSET
str r1, [r0, r2]
#define CORE_TIMERS_RATIO 0x1
#define COPY2_RATIO 0x3
#define DMCP_RATIO 0x1
#define DMCD_RATIO 0x1
#define DMC_RATIO 0x1
#define DPHY_RATIO 0x1
#define ACP_PCLK_RATIO 0x1
#define ACP_RATIO 0x3
#define CLK_DIV_DMC0_VAL ((CORE_TIMERS_RATIO << 28) \
| (COPY2_RATIO << 24) \
| (DMCP_RATIO << 20) \
| (DMCD_RATIO << 16) \
| (DMC_RATIO << 12) \
| (DPHY_RATIO << 8) \
| (ACP_PCLK_RATIO << 4) \
| (ACP_RATIO))
#define DPM_RATIO 0x1
#define DVSEM_RATIO 0x1
#define PWI_RATIO 0x1
#define CLK_DIV_DMC1_VAL ((DPM_RATIO << 24) \
| (DVSEM_RATIO << 16) \
| (PWI_RATIO << 8))
接下来分别对 CMU_TOP、CMU_LEFTBUS、CMU_RIGHTBUS的时钟源和分频比进行了设置:
@ CMU_TOP MUX / DIV
ldr r1, =0x0
ldr r2, =CLK_SRC_TOP0_OFFSET
str r1, [r0, r2]
ldr r2, =CLK_MUX_STAT_TOP_OFFSET
ldr r3, =0x11111111
bl wait_mux_state
ldr r1, =0x0
ldr r2, =CLK_SRC_TOP1_OFFSET
str r1, [r0, r2]
ldr r2, =CLK_MUX_STAT_TOP1_OFFSET
ldr r3, =0x01111110
bl wait_mux_state
ldr r1, =CLK_DIV_TOP_VAL
ldr r2, =CLK_DIV_TOP_OFFSET
str r1, [r0, r2]
@ CMU_LEFTBUS MUX / DIV
ldr r1, =0x10
ldr r2, =CLK_SRC_LEFTBUS_OFFSET
str r1, [r0, r2]
ldr r2, =CLK_MUX_STAT_LEFTBUS_OFFSET
ldr r3, =0x00000021
bl wait_mux_state
ldr r1, =CLK_DIV_LEFRBUS_VAL
ldr r2, =CLK_DIV_LEFTBUS_OFFSET
str r1, [r0, r2]
@ CMU_RIGHTBUS MUX / DIV
ldr r1, =0x10
ldr r2, =CLK_SRC_RIGHTBUS_OFFSET
str r1, [r0, r2]
ldr r2, =CLK_MUX_STAT_RIGHTBUS_OFFSET
ldr r3, =0x00000021
bl wait_mux_state
ldr r1, =CLK_DIV_RIGHTBUS_VAL
ldr r2, =CLK_DIV_RIGHTBUS_OFFSET
str r1, [r0, r2]
接下来设置Lock Time:配置完PLL锁相环后会有一段时钟频率为0的空挡,这段时间CPU不工作,这段时间称为Lock Time
@ Set PLL locktime
ldr r1, =APLL_LOCK_VAL
ldr r2, =APLL_LOCK_OFFSET
str r1, [r0, r2]
ldr r1, =MPLL_LOCK_VAL
ldr r2, =MPLL_LOCK_OFFSET
str r1, [r0, r2]
ldr r1, =EPLL_LOCK_VAL
ldr r2, =EPLL_LOCK_OFFSET
str r1, [r0, r2]
ldr r1, =VPLL_LOCK_VAL
ldr r2, =VPLL_LOCK_OFFSET
str r1, [r0, r2]
ldr r1, =CLK_DIV_CPU0_VAL
ldr r2, =CLK_DIV_CPU0_OFFSET
str r1, [r0, r2]
ldr r1, =CLK_DIV_CPU1_VAL
ldr r2, =CLK_DIV_CPU1_OFFSET
str r1, [r0, r2]
/* APLL_LOCK */
#define APLL_LOCK_VAL 0x000002F1
#define APLL_LOCK_OFFSET 0x14000
所以是向APLL_LOCK寄存器中写入0x0000_02F1接下来设置锁相环:
@ Set APLL
ldr r1, =APLL_CON1_VAL
ldr r2, =APLL_CON1_OFFSET
str r1, [r0, r2]
ldr r1, =APLL_CON0_VAL
ldr r2, =APLL_CON0_OFFSET
str r1, [r0, r2]
APLL,MPLL,EPLL,VPLL设置方法类似,所以这里只看APLL即可
向APLL_CON1写入0x0080_3800,可以看第22位BYPASS,如果置1则APLL输出直接是FIN的输入,所以设置为0输出为倍频后的时钟:
向APLL_CON0写入APLL_CON0_VAL,先来看一下APLL_CON0寄存器的描述:
31bit用来使能和失效APLL,如果要使用肯定是置1;16 ~25bit,8 ~13bit,0 ~2bit分别代表着M,P,S三个值,而这三个值用来配置具体输出频率(根据公式)
FOUT = M * FIN / (P * 2^S)
下表是一些推荐的配置(可以验证下上面的公式)即可:
U-boot源码中传入参数是M=200,P=6,S=1,得到FOUT = 400
APLL_CON0的29bit用来读出PLL的锁定状态,如果是1表示已锁定(即PLL输出稳定),所以在设定完APLL后跳转到wai_pll_lock标号循环等待是否稳定:
ldr r2, =APLL_CON0_OFFSET
bl wait_pll_lock
wait_pll_lock:
ldr r1, [r0, r2]
tst r1, #(1<<29)
beq wait_pll_lock
mov pc, lr
在设置好PLL之后,此时设置MUX都选择PLL输出的时钟频率为输入,以CMU_CPU选择为例(CMU_DMC,CMU_TOP等操作相似):