s3c2410处理器系统时钟包括FCLK,HCLK和PCLK,它们一起由MPLL分频提供。
FCLK:CPU时钟,就是处理器的主频。常说的s3c2410A能上200MHz,s3c2440A能上400MHz就是指它。它为ARM920T输入。
HCLK:为AHB总线外围时钟,为ARM920T、内存控制器、中断控制器、LCD控制器、DMA和USB Host输入。
PCLK:为APB总线外围时钟,为WDT,I2S,I2C,PWMtimer,MMC接口,ADC,UART,GPIO,RTC和SPI等片内外围设备输入。
当Main PLL锁定之后,FCLK=Mpll,而HCLK,PCLK分别由HDIVN,PDIVN确定是否由FCLK分频。
锁相环是用来产生时钟频率的,s3c2440有两个锁相环:MPLL和UPLL。
MPLL是用来产生FCLK,HCLK,PCLK三种时钟频率的,FCLK是CPU的时钟信号,HCLK是AHB总线的时钟信号,PCLK是APB总线的时钟信号。UPLL则是用于产生USB的时钟。下面主要讲述FCLK,HCLK,PCLK时钟频率的获得。
通过设置MPLL锁相环就可以得到CPU的时钟信号FCLK,而MPLL的值依赖于mdiv,pdiv,sdiv的值
void ChangeMPllValue(int mdiv,int pdiv,int sdiv)
{
rMPLLCON = (mdiv<<12) | (pdiv<<4) | sdiv;
}
CPU时钟信号FCLK的频率计算公式为:
FCLK=(2*m*FIN)/(p*2^s)
其中 m=(mdiv+8), p=(pdiv+2), s=sdiv,
FIN为输入时钟的频率,通过设置OM[3:2]来选择是用外部晶振还是外部时钟。要获得HCLK和PCLK的值,则还需设置分频器,即CLKDIVN寄存器。
若设置为[2:1]=01,[0]=1,则FCLK:HCLK:PCLK的分频系数之比为1:2:4,即FCLK,HCLK,PCLK的频率为:FCLK,FCLK/2,FCLK/4。
例:
MPLLCON:设为(0x5c << 12)|(0x04 << 4)|(0x00),即0x5c040
对于MPLLCON寄存器,[19:12]为MDIV,[9:4]为PDIV,[1:0]为SDIV。
根据计算公式:
FCLK = (2*m * Fin)/(p * 2^s)
其中: m = MDIV + 8 = 92+8 = 100 , p = PDIV + 2 = 4+2 = 6, s = 0,Fin=12MHz(无源晶振频率12MHz)。
则FCLK= (2*100*12)/(6*2^0) = 400 MHz
若设置分频器CLKDIVN的[2:1]=01,[0]=1,则HCLK=200MHz,PCLK=100MHz。
在Linux中没有设置S3C6410的CPU时钟,而是在u-boot(uboot1.1.6/include/configs/smdk6410.h)中设置好的:
//#define CONFIG_CLK_800_133_66
//#define CONFIG_CLK_666_133_66
//#define CONFIG_CLK_532_133_66
#define CONFIG_CLK_400_133_66
//#define CONFIG_CLK_400_100_50
//#define CONFIG_CLK_OTHERS
在Linux的时钟频率代码(linux2.6.28/arch/arm/plat-s3c64xx/s3c6400-clock.c)中也只是直接从PLL寄存器中读取:
void __init_or_cpufreq s3c6400_setup_clocks(void)
{
***
xtal = clk_get_rate(xtal_clk);
clk_put(xtal_clk);
printk(KERN_DEBUG "%s: xtal is %ld\n", __func__, xtal);
epll = s3c6400_get_epll(xtal);
mpll = s3c6400_get_pll(xtal, __raw_readl(S3C_MPLL_CON));
apll = s3c6400_get_pll(xtal, __raw_readl(S3C_APLL_CON));
fclk = apll / GET_DIV(clkdiv0, S3C6410_CLKDIV0_ARM);
printk(KERN_INFO "S3C64XX: PLL settings, A=%ld, M=%ld, E=%ld\n",
apll, mpll, epll);
if(__raw_readl(S3C_OTHERS) & S3C_OTHERS_SYNCMUXSEL_SYNC) {
/* Synchronous mode */
hclkx2 = apll / GET_DIV(clkdiv0, S3C6400_CLKDIV0_HCLK2);
} else {
/* Asynchronous mode */
hclkx2 = mpll / GET_DIV(clkdiv0, S3C6400_CLKDIV0_HCLK2);
}
hclk = hclkx2 / GET_DIV(clkdiv0, S3C6400_CLKDIV0_HCLK);
pclk = hclkx2 / GET_DIV(clkdiv0, S3C6400_CLKDIV0_PCLK);
printk(KERN_INFO "S3C64XX: HCLKx2=%ld, HCLK=%ld, PCLK=%ld\n", hclkx2, hclk, pclk);
}
S3C6410 nandflash的分区表:arch/arm/plat-s3c/include/plat/partition.h