S5PV210时钟方式
外部晶振+内部时钟发生器+内部PLL产生高频时钟+内部分频器分频得到各种频率的时钟
时钟域概念
因为S5PV210的时钟体系比较复杂,内部外设模块太多,所以把整个内部的时钟分为3个时钟域。如下图:
- MSYS Domain: CPU(Cortex-A8内核)、DRAM控制器(DMC0和DMC1)、IRAM和IROM、3D模块等等。
- DSYS Domain: 与视频显示、编解码相关的模块,如HDMI
PSYS Domain : 和内部的各种外设时钟有关,譬如串口、SD接口、I2C、SPI、USB等。
S5PV210 把时钟分为3个时钟域是为了把高低速时钟频率相互独立开,能够适应各种内设和外设模块。
时钟来源
S5PV210外部有4个时钟接口,可以接4个外部晶振共SoC使用,SoC接上外部晶振上电后就可产生原始时钟,继而进行倍频产生高频时钟,再经过各种分频,产生各个模块需要的高速和低速时钟。如下图所示:
其中:
XRTCXT1:用于产生时钟频率,32.768KHz,通常用于时钟频率,也可用于System Timer,比较少用。
XXTI:用于系统定时器,长用于产生系统时钟,官方推荐使用24MHz的晶振。因为IROM被设计成基于24MHz的时钟输入。
XUSBXTI:长用于USB时钟,也可用于系统定时器,产生系统时钟,官方推荐使用24MHz的晶振。因为IROM被设计成基于24MHz的时钟输入。
XHDMIXTI:专用于HDMI模块,产生HDMI时钟,官方推荐使用27MHz的晶振。,因为VPLL或HDMI PHY 用于TV解码需要产生54MHz的时钟。
24MHz的时钟源经过SoC的倍频后会产生4个倍频器,分别是:APLL、MPLL、EPLL、VPLL。
APLL产生的倍频范围是:30MHz~1GHz
MPLL产生的倍频范围是:50MHz~2GHz
EPLL产生的倍频范围是:10MHz~600MHz
VPLL产生的倍频范围是:10MHz~600MHz
通常USB-OTG PHY使用XUSBXTI产生30MHz和48MHz,HDMI PHY使用XUSBXTI或XHDIMXTI产生54MHz
在S5PV210的应用中,通常Cortex A8内核和MSYS Clock时钟域使用APLL倍频
DSYS 时钟域和PSYS 时钟域和其他外设使用MPLL和EPLL倍频
而视频时钟则使用VPLL倍频
S5PV210的三个时钟域详解
S5PV210内部的各个外设都是接在(内部的AMBA总线)总线上的,AMBA总线有一条高频分支AHB和一条低频分支APB。
每个域都有各自对应的高频分支和低频分支,分别是:HCLK_XXX和HCLK_XXX,HCLK_XXX是高频分支,PCLK_XXX是低频分支。
在IROM代码中执行时第6步中初始化了时钟系统,这时给了系统一个默认推荐的运行频率。
这个时钟频率是三星推荐的S5PV210工作性能和稳定性最好的频率。
时钟框图详细
- 时钟分频框图
各个外设的详细时钟分频框图
第一张图从左到右依次完成了原始时钟生产->PLL倍频得到高频时钟。
第二张图从各中间时钟(第一张图中某个某个步骤生成的时钟)到各外设自己使用的外设(实际就是个别外设自己再额外分频的设置)
图中两个比较重要的符号的作用,MUX开关和DIV分频器
MUX开关就是个或门,实际对应摸个寄存器的某几个bit位的位置,设置值决定了哪条通道通的,分析这个可以知道右边的时钟是从哪条路来的,从而知道右边时钟是多少。
时钟初始化代码
// 时钟控制器基地址
#define ELFIN_CLOCK_POWER_BASE 0xE0100000
// 时钟相关的寄存器相对时钟控制器基地址的偏移值
#define APLL_LOCK_OFFSET 0x00
#define MPLL_LOCK_OFFSET 0x08
#define APLL_CON0_OFFSET 0x100
#define APLL_CON1_OFFSET 0x104
#define MPLL_CON_OFFSET 0x108
#define CLK_SRC0_OFFSET 0x200
#define CLK_SRC1_OFFSET 0x204
#define CLK_SRC2_OFFSET 0x208
#define CLK_SRC3_OFFSET 0x20c
#define CLK_SRC4_OFFSET 0x210
#define CLK_SRC5_OFFSET 0x214
#define CLK_SRC6_OFFSET 0x218
#define CLK_SRC_MASK0_OFFSET 0x280
#define CLK_SRC_MASK1_OFFSET 0x284
#define CLK_DIV0_OFFSET 0x300
#define CLK_DIV1_OFFSET 0x304
#define CLK_DIV2_OFFSET 0x308
#define CLK_DIV3_OFFSET 0x30c
#define CLK_DIV4_OFFSET 0x310
#define CLK_DIV5_OFFSET 0x314
#define CLK_DIV6_OFFSET 0x318
#define CLK_DIV7_OFFSET 0x31c
#define CLK_DIV0_MASK 0x7fffffff
// 这些M、P、S的配置值都是查数据手册中典型时钟配置值的推荐配置得来的。
// 这些配置值是三星推荐的,因此工作最稳定。如果是自己随便瞎拼凑出来的那就要
// 经过严格测试,才能保证一定对。
#define APLL_MDIV 0x7d // 125
#define APLL_PDIV 0x3
#define APLL_SDIV 0x1
#define MPLL_MDIV 0x29b // 667
#define MPLL_PDIV 0xc
#define MPLL_SDIV 0x1
#define set_pll(mdiv, pdiv, sdiv) (1<<31 | mdiv<<16 | pdiv<<8 | sdiv)
#define APLL_VAL set_pll(APLL_MDIV,APLL_PDIV,APLL_SDIV)
#define MPLL_VAL set_pll(MPLL_MDIV,MPLL_PDIV,MPLL_SDIV)
#define REG_CLK_SRC0 (ELFIN_CLOCK_POWER_BASE + CLK_SRC0_OFFSET)
#define REG_APLL_LOCK (ELFIN_CLOCK_POWER_BASE + APLL_LOCK_OFFSET)
#define REG_MPLL_LOCK (ELFIN_CLOCK_POWER_BASE + MPLL_LOCK_OFFSET)
#define REG_CLK_DIV0 (ELFIN_CLOCK_POWER_BASE + CLK_DIV0_OFFSET)
#define REG_APLL_CON0 (ELFIN_CLOCK_POWER_BASE + APLL_CON0_OFFSET)
#define REG_MPLL_CON (ELFIN_CLOCK_POWER_BASE + MPLL_CON_OFFSET)
#define rREG_CLK_SRC0 (*(volatile unsigned int *)REG_CLK_SRC0)
#define rREG_APLL_LOCK (*(volatile unsigned int *)REG_APLL_LOCK)
#define rREG_MPLL_LOCK (*(volatile unsigned int *)REG_MPLL_LOCK)
#define rREG_CLK_DIV0 (*(volatile unsigned int *)REG_CLK_DIV0)
#define rREG_APLL_CON0 (*(volatile unsigned int *)REG_APLL_CON0)
#define rREG_MPLL_CON (*(volatile unsigned int *)REG_MPLL_CON)
void clock_init(void)
{
// 1 设置各种时钟开关,暂时不使用PLL
rREG_CLK_SRC0 = 0x0;
// 2 设置锁定时间,使用默认值:0x0FFF,保险起见我们设置为0xFFFF
// 设置PLL后,时钟从Fin提升到目标频率时,需要一定的时间,即锁定时间
rREG_APLL_LOCK = 0x0000ffff;
rREG_MPLL_LOCK = 0x0000ffff;
// 3 设置分频
// 清bit[0~31]
rREG_CLK_DIV0 = 0x14131440;
// 4 设置PLL
// FOUT = MDIV*FIN/(PDIV*2^(SDIV-1))=0x7d*24/(0x3*2^(1-1))=1000 MHz
rREG_APLL_CON0 = APLL_VAL;
// FOUT = MDIV*FIN/(PDIV*2^SDIV)=0x29b*24/(0xc*2^1)= 667 MHz
rREG_MPLL_CON = MPLL_VAL;
// 5 设置各种时钟开关,使用PLL
rREG_CLK_SRC0 = 0x10001111;
}
时钟设置的寄存器详解
在寄存器中,clock source x - 设置MUX开关,clock divider control - 设置分频系数
涉及到的寄存器,可以参考官方数据手册里的section 02_system -> 3 CLOCK CONTROLLER ->3.7章节的内容。
xPLL_LOCK 寄存器:主要控制PLL锁定周期的。x : A/M/E/V
xPLL_CON/xPLL_CON0/xPLL_CON1 寄存器:主要是用来打开/关闭PLL电路,设置PLL的倍频产生,查看PLL锁定状态等
三星官方推荐设置参数:
APLL_CON0和APLL_CON1寄存器:
APLL_CON1寄存器暂时未用到
CLK_SRCn(n:0~6)寄存器:用来设置时钟源的,对应时钟框图中的MUX开关。
CLK_SRC_MASKn(n:0~1)寄存器:决定MUX开关n选1后是否能继续通过。默认的时钟都是打开的。
CLK_DIVn(n:0~7)寄存器:设置各模块的参数配置
CLK_GATE_x寄存器:类似于CLK_SRC_MASK,对时钟进行开关控制。不常用。
CLK_DIV_STATn(n:0~1)寄存器:查看DIV的状态是否已经完成还是在进行中
CLK_MUX_STATn(n:0~1)寄存器:查看MUX的状态是否已经完成还是在进行中
总结:最重要的有三类寄存器,分别是CON、SRC、DIV。其中CON决定PLL倍频多少,SRC决定走哪一条路,DIV决定分频多少。