1.设置APLL/MPLL/EPLL/EPLL锁相环时间
翻看手册,P371页,找到PLL CONTROL REGISTERS。
• (APLL_LOCK, R/W, Address = 0xE010_0000)
• (MPLL_LOCK, R/W, Address = 0xE010_0008)
• (EPLL_LOCK, R/W, Address = 0xE010_0010)
• (VPLL_LOCK, R/W, Address = 0xE010_0020)
这里出现了一个问题,APLL/MPLL/EPLL/VPLL的锁相环的时间是不一样的,而像S5PC100,频率为667MHz,他的A/M/E/HPLL的锁相环时间均是300us,而在Exynos 4xxx中,时间又变成了250,这个时间一定要查芯片手册。那么,所以,这里我们要用4个寄存器来存不同的LOCK_OFFSET的值。查询ATPCS文档(Procedure Call Standard for the ARM v2.08),在第P15页找到,r4-r8是用来存储变量的寄存器,OK,我们选取4个寄存器来保存值。
APLL的锁时间为:24*30=720 --0x2D0
MPLL的锁时间为:24*200=4800 --0x12C0
EPLL的锁时间为:24*375=9000 --0x2328
VPLL的锁时间为:24*100=2400 --0x960
将上面四个值存入寄存器r4--r8,这里使用寄存器的别名v1--v4,然后再将寄存值的分别存入APLL_LOCK/MPLL_LOCK/EPLL_LOCK/VPLL_LOCK对应的寄存器地址中
ldr r0, =0xE0100000 /*ELFIN_CLOCK_POWER_BASE*/
ldr v1, =0x2D0
ldr v2, =0x12C0
ldr v3, =0x2328
ldr v4, =0x960
str v1, [r0, #0x00] /*APLL_LOCK_OFFSET*/
str v2, [r0, #0x04] /*MPLL_LOCK_OFFSET*/
str v3, [r0, #0x08] /*EPLL_LOCK_OFFSET*/
str v4, [r0, #0x0c] /*VPLL_LOCK_OFFSET*/
2.1倍频APLL
我们只要把
P=3 M=125,S=1即可让输出频率为1000MHz,即MSOUT_MSYS=1000MHz,那么配置哪个寄存器可以倍频呢?继续向下看手册,在P372页找到APLL_CON0(
0xE010_0100)。
上面这个表显示出如何配置P/M/S位,那么如下代码,我们可以配置输出为2000MHz。(参考代码board/samsung/smdkc100/lowlevel_init.S --
system_clock_init)
ldr r1, =(1<<31 |125<<16 |3<<8 |1<<0) @APLL_VAL=(1<<31 | 445<<16 | 0x4<<8 | 1<<0)
/*APLL_CON0_OFFSET 0x100*/
str r1, [r0, #APLL_CON_OFFSET] @24 1000 APLL 3 125 1 2000.0 FOUTAPLL = 1000.0MHz
2.2倍频MPLL
MPLL的P/M/S位配置为P=12 M=667,S=1,按上面的配置为MPLL_CON寄存器,Address=0xE010_0108
ldr r1, =(1<<31 |667<<16 |12<<8 |1<<0) @MPLL_VAL=(1<<31 |667<<16 |12<<8 |1<<0)
/*MPLL_CON0_OFFSET 0x108*/
str r1, [r0, #MPLL_CON_OFFSET] @24 667 MPLL 12 667 1 667.0 FOUTAPLL = 667.0MHz
2.3倍频EPLL
EPLL的P/M/S位配置为P=3 M=48,S=2,按上面的配置配置EPLL_CON寄存器,Address=0xE010_0110
ldr r1, =(1<<31 |108<<16 |6<<8 |3<<0) @VPLL_VAL=(1<<31 |108<<16 |6<<8 |3<<0)
/*VPLL_CON0_OFFSET 0x120*/
str r1, [r0, #VPLL_CON_OFFSET] @24 54 VPLL 6 108 3 54.0 FOUTAPLL = 54.0MHz
OK,倍频完成,代码整理一下:
ldr r1, =(1<<31 |125<<16 |3<<8 |1<<0) @APLL_VAL=(1<<31 | 445<<16 | 0x4<<8 | 1<<0)
/*APLL_CON0_OFFSET 0x100*/
str r1, [r0, #APLL_CON_OFFSET] @24 1000 APLL 3 125 1 2000.0 FOUTAPLL = 1000.0MHz
ldr r1, =(1<<31 |667<<16 |12<<8 |1<<0) @MPLL_VAL=(1<<31 |667<<16 |12<<8 |1<<0)
/*MPLL_CON0_OFFSET 0x108*/
str r1, [r0, #MPLL_CON_OFFSET] @24 667 MPLL 12 667 1 667.0 FOUTAPLL = 667.0MHz
ldr r1, =(1<<31 |48<<16 |3<<8 |2<<0) @EPLL_VAL=(1<<31 |48<<16 |3<<8 |2<<0)
/*EPLL_CON0_OFFSET 0x110*/
str r1, [r0, #EPLL_CON_OFFSET] @24 96 EPLL 3 48 2 96.0 FOUTAPLL = 96.0MHz
ldr r1, =(1<<31 |108<<16 |6<<8 |3<<0) @VPLL_VAL=(1<<31 |108<<16 |6<<8 |3<<0)
/*VPLL_CON0_OFFSET 0x120*/
str r1, [r0, #VPLL_CON_OFFSET] @24 54 VPLL 6 108 3 54.0 FOUTAPLL = 54.0MHz
参考芯片手册P361页时钟生成电路图,系统的MSYS/DSYS/PSYS的CLK全部来自于APLL和MPLL,现在开始对APLL/MPLL进行分频,分给不同的设备,如Cotrex A8内核或其它设备。在分频之前,必须要先选择时钟源,否则怎么分频呢?
3.选择时钟源
参考芯片手册P361页时钟生成电路图,以APLL以例,查找FOUTAPLL和FINPLL
由图所知,FINPLL是没有倍频的频率,我们要选的是倍频后的,所以应该选择FOUTAPLL,依此类推,后面几位选上FOUTMPLL/FOUTEPLL/FOUTVPLL。那第[28]位,ONENADN选择什么呢?往上查一下ONENAND用的是什么CLK,在P363页查找到用的是PSYS,所以这里应该选择0。
//CLK_SRC0_OFFSET 0x200
ldr r1, [r0, #CLK_SRC0_OFFSET]
ldr r2, =(0<<28|1<<12 |1<<8 |1<<4 |1<<0)
orr r1, r1, r2
str r1, [r0, #CLK_SRC0_OFFSET]
OK,现在开始对APLL/MPLL进行分频了。
4.分频
继续向下查找芯片手册,找到P387页,系统时钟分频操作的寄存器是CLK_DIV0,Address =
0xE010_0300
回过头去,再去看时钟频率的产生图,最终MSYS有五个CLK,
ARMCLK/HCLK_MSYS/PCLK_MSYS/HCLK_IMEM/SCLK_DMC0。P356查看常用CLK
Values for the high-performance operation:
• freq(ARMCLK) = 1000 MHz
• freq(HCLK_MSYS) = 200 MHz
• freq(HCLK_IMEM) = 100 MHz
• freq(PCLK_MSYS) = 100 MHz
• freq(HCLK_DSYS) = 166 MHz
• freq(PCLK_DSYS) = 83 MHz
• freq(HCLK_PSYS) = 133 MHz
• freq(PCLK_PSYS) = 66 MHz
• freq(SCLK_ONENAND) = 133 MHz, 166
在寄存器CLK_DIV0的表格上还有这样一段话:There are operating frequency limitations. The maximum operating frequency of SCLKAPLL, SCLKMPLL, SCLKA2M, HCLK_MSYS, and PCLK_MSYS are 1GHz, 667 MHz, 400 MHz, 200 MHz, and 100 MHz, respectively. These operating clock conditions must be met through CLK_DIVX configuration.
查找SCLKAPLL,找到SCLKAPLL的来源
OK,将上面列表中CLK也整理一下,列表出来:
• freq(SCLKAPLL) = 1000 MHz
• freq(SCLKMPLL) = 667 MHz
• freq(SCLKA2M) = 400 MHz
接着分析图,在[19:16]要用到的MSOUT_DSYS,他的频率来自MPLL,所以MSOUT_DSYS =MPLL = 667
下面一位一位的开始分析:
[2:0]ARMCLK = 1000MHz,MOUT_MSYS = 2000MHz,ARMCLK = MOUT_MSYS/(1+1),APLL_RATIO为1,1<<0
[6:4]SCLKA2M = 400MHz,SCLKAPLL = 1000MHz,由于无法整除,而SCLKA2M最大为400,我们只要保证不超过就行,可以低一些,SCLKA2M = SCLKAPLL / (2 + 1) = 333,A2M_RATIO为2,2<<4
[10:8]HCLK_MSYS = 200MHz,ARMCLK = 1000MHz,HCLK_MSYS = ARMCLK /(4 + 1),HCLK_MSYS_RATIO为4,4<<8
[14:12]PCLK_MSYS = 100MHz,HCLK_MSYS = 200MHz,PCLK_MSYS = HCLK_MSYS / (1 + 1) ,PCLK_MSYS_RATIO为1,1<<12
[19:16]HCLK_DSYS = 166MHz,MOUT_DSYS = 667MHz,HCLK_DSYS = MOUT_DSYS / (3+ 1) ,HCLK_DSYS_RATIO为1,3<<16
[22:20]PCLK_DSYS = 83MHz,HCLK_DSYS = 166MHz,PCLK_DSYS = HCLK_DSYS / (1 + 1) ,PCLK_DSYS_RATIO为1,1<<20
[27:24]HCLK_PSYS = 133MHz,MOUT_PSYS = 667MHz,HCLK_PSYS = MOUT_PSYS / (4 + 1) ,HCLK_PSYS_RATIO为1,4<<24
[30:28]PCLK_PSYS = 66MHz,HCLK_PSYS = 133MHz,PCLK_PSYS = HCLK_PSYS / (1 + 1) ,PCLK_PSYS_RATIO为1,1<<28
OK,至此,分频完成,整理代码如下:
/*将r0的值偏移300,此时地址为CLK_DIV0 = 0xE010_0300),保存到r1中*/
ldr r1, [r0, #0x300] /*CLK_DIV0_OFFSET*/
/*将CLK_DIV0中有关分频的位全部清零0~30*/
ldr r2, =0x7fff /*CLK_DIV0_MASK*/
bic r1, r1, r2
/*分频给系统时钟*/
ldr r2, =(0<<0)|(2<<4)|(4<<8)|(1<<12)|(3<<16)|(1<<20)|(4<<24)|(1<<28)
orr r1, r1, r2
str r1, [r0, #0x300] //CLK_DIV0_OFFSET
5.延时一段时间
/*delay*/
mov r1, #0x10000
1: subs r1, r1, #1
bne 1b
mov pc, lr
OK,到此,系统时钟频率的全部设置工作完成。最后将代码重新归整如下:
system_clock_init:
ldr r0, =ELFIN_CLOCK_POWER_BASE @0xe0100000
ldr v1, =0x2D0
ldr v2, =0x12C0
ldr v3, =0x2328
ldr v4, =0x960
str v1, [r0, #0x00] /*APLL_LOCK_OFFSET*/
str v2, [r0, #0x04] /*MPLL_LOCK_OFFSET*/
str v3, [r0, #0x08] /*EPLL_LOCK_OFFSET*/
str v4, [r0, #0x0c] /*VPLL_LOCK_OFFSET*/
/**************************************************************************
APLL_CON0_OFFSET 0x100 @24 1000 APLL 3 125 1 2000.0 FOUTAPLL = 1000.0MHz
MPLL_CON0_OFFSET 0x108 @24 667 MPLL 12 667 1 667.0 FOUTAPLL = 667.0MHz
EPLL_CON0_OFFSET 0x110 @24 54 VPLL 6 108 3 54.0 FOUTAPLL = 54.0MHz
***************************************************************************/
ldr r1, =(1<<31 |125<<16 |3<<8 |1<<0) @APLL_VAL=(1<<31 | 445<<16 | 0x4<<8 | 1<<0)
str r1, [r0, #APLL_CON0_OFFSET]
ldr r1, =(1<<31 |667<<16 |12<<8 |1<<0) @MPLL_VAL=(1<<31 |667<<16 |12<<8 |1<<0)
str r1, [r0, #MPLL_CON_OFFSET]
ldr r1, =(1<<31 |48<<16 |3<<8 |2<<0) @EPLL_VAL=(1<<31 |48<<16 |3<<8 |2<<0)
str r1, [r0, #EPLL_CON_OFFSET]
ldr r1, =(1<<31 |108<<16 |6<<8 |3<<0) @VPLL_VAL=(1<<31 |108<<16 |6<<8 |3<<0)
str r1, [r0, #VPLL_CON_OFFSET]
//CLK_SRC0_OFFSET 0x200
ldr r1, [r0, #CLK_SRC0_OFFSET]
ldr r2, =(0<<28|1<<12 |1<<8 |1<<4 |1<<0)
orr r1, r1, r2
str r1, [r0, #CLK_SRC0_OFFSET]
/*将r0的值偏移300,此时地址为CLK_DIV0 = 0xE010_0300,保存到r1
* 将CLK_DIV0中有关分频的位全部清零0~30
*/
ldr r1, [r0, #0x300] /*CLK_DIV0_OFFSET*/
ldr r2, =0x7fff /*CLK_DIV0_MASK*/
bic r1, r1, r2
/*分频给系统时钟*/
ldr r2, =(0<<0)|(2<<4)|(4<<8)|(1<<12)|(3<<16)|(1<<20)|(4<<24)|(1<<28)
orr r1, r1, r2
str r1, [r0, #0x300] //CLK_DIV0_OFFSET
mov r1, #0x10000
1: subs r1, r1, #1
bne 1b
mov pc, lr