内核时钟初始化函数是s3c24xx_init_clocks(12000000)。
改函数的从start_kernel-->setup_arch(&command_line)-->paging_init(mdesc)-->devicemaps_init(mdesc)-->if (mdesc->map_io)mdesc->map_io();-->mini2440_map_io-->s3c24xx_init_clocks(12000000).所以此函数是各个部分初始化的起点。
s3c24xx_init_clocks(12000000)定义如下:
--------------------------------------------------------------------------------------------------
void __init s3c24xx_init_clocks(int xtal)
{
if (xtal == 0)
xtal = 12*1000*1000;
if (cpu == NULL)
panic("s3c24xx_init_clocks: no cpu setup?\n");
if (cpu->init_clocks == NULL)
panic("s3c24xx_init_clocks: cpu has no clock init\n");
else
(cpu->init_clocks)(xtal);
}
--------------------------------------------------------------------------------------------------
这个函数中看到一个cpu,那么这个变量是什么。看文件plat-s3c/init.c:
static struct cpu_table *cpu;它是一个cpu_table类型的指针。如下是其结构定义:
--------------------------------------------------------------------------------------------------
struct cpu_table {
unsigned long idcode;
unsigned long idmask;
void (*map_io)(void);
void (*init_uarts)(struct s3c2410_uartcfg *cfg, int no);
void (*init_clocks)(int xtal);
int (*init)(void);
const char *name;
};
--------------------------------------------------------------------------------------------------
这样就不难理解s3c24xx_init_clocks了。那么这个CPU指针具体指向什么呢。在文件cpu.c中有如下定义:
static const char name_s3c2400[] = "S3C2400";
static const char name_s3c2410[] = "S3C2410";
static const char name_s3c2412[] = "S3C2412";
static const char name_s3c2440[] = "S3C2440";
static const char name_s3c2442[] = "S3C2442";
static const char name_s3c2442b[] = "S3C2442B";
static const char name_s3c2443[] = "S3C2443";
static const char name_s3c2410a[] = "S3C2410A";
static const char name_s3c2440a[] = "S3C2440A";
static struct cpu_table cpu_ids[] __initdata = {
{
.idcode = 0x32410000,
.idmask = 0xffffffff,
.map_io = s3c2410_map_io,
.init_clocks = s3c2410_init_clocks,
.init_uarts = s3c2410_init_uarts,
.init = s3c2410_init,
.name = name_s3c2410
},
{
.idcode = 0x32410002,
.idmask = 0xffffffff,
.map_io = s3c2410_map_io,
.init_clocks = s3c2410_init_clocks,
.init_uarts = s3c2410_init_uarts,
.init = s3c2410a_init,
.name = name_s3c2410a
},
{
.idcode = 0x32440000,
.idmask = 0xffffffff,
.map_io = s3c244x_map_io,
.init_clocks = s3c244x_init_clocks,
.init_uarts = s3c244x_init_uarts,
.init = s3c2440_init,
.name = name_s3c2440
},
{
.idcode = 0x32440001,
.idmask = 0xffffffff,
.map_io = s3c244x_map_io,
.init_clocks = s3c244x_init_clocks,
.init_uarts = s3c244x_init_uarts,
.init = s3c2440_init,
.name = name_s3c2440a
},
{
.idcode = 0x32440aaa,
.idmask = 0xffffffff,
.map_io = s3c244x_map_io,
.init_clocks = s3c244x_init_clocks,
.init_uarts = s3c244x_init_uarts,
.init = s3c2442_init,
.name = name_s3c2442
},
{
.idcode = 0x32440aab,
.idmask = 0xffffffff,
.map_io = s3c244x_map_io,
.init_clocks = s3c244x_init_clocks,
.init_uarts = s3c244x_init_uarts,
.init = s3c2442_init,
.name = name_s3c2442b
},
{
.idcode = 0x32412001,
.idmask = 0xffffffff,
.map_io = s3c2412_map_io,
.init_clocks = s3c2412_init_clocks,
.init_uarts = s3c2412_init_uarts,
.init = s3c2412_init,
.name = name_s3c2412,
},
{
.idcode = 0x32412003,
.idmask = 0xffffffff,
.map_io = s3c2412_map_io,
.init_clocks = s3c2412_init_clocks,
.init_uarts = s3c2412_init_uarts,
.init = s3c2412_init,
.name = name_s3c2412,
},
{
.idcode = 0x32443001,
.idmask = 0xffffffff,
.map_io = s3c2443_map_io,
.init_clocks = s3c2443_init_clocks,
.init_uarts = s3c2443_init_uarts,
.init = s3c2443_init,
.name = name_s3c2443,
},
{
.idcode = 0x0,
.idmask = 0xffffffff,
.map_io = s3c2400_map_io,
.init_clocks = s3c2400_init_clocks,
.init_uarts = s3c2400_init_uarts,
.init = s3c2400_init,
.name = name_s3c2400
},
};
在这里,我们只关心.name为.name = name_s3c2440的数组元素。这里.init_clocks部位NULL,那么函数s3c24xx_init_clocks中的(cpu->init_clocks)(xtal)将被运行。
接着就到了s3c244x.c中的函数s3c244x_init_clocks:
---------------------------------------------------------
void __init s3c244x_init_clocks(int xtal)
{
s3c24xx_register_baseclocks(xtal);
s3c244x_setup_clocks();
s3c2410_baseclk_add();
}
--------------------------------------------------------------------------------------------------
在s3c244x_init_clocks函数里,有三个函数
s3c24xx_register_baseclocks(xtal);这个函数用来注册clk_xtal,clk_mpll,clk_upll,clk_f,clk_h,clk_p六个clk类型的时钟。
s3c244x_setup_clocks()是时钟的设置。
s3c2410_baseclk_add()添加不同部件的时钟,以便驱动中使用。后面会相信分析。
下面一个一个来分析他们这三个函数到底做了什么工作。
s3c24xx_register_baseclocks(xtal)函数定义如下:
-------------------------------------------------
int __init s3c24xx_register_baseclocks(unsigned long xtal)
{
printk(KERN_INFO "S3C24XX Clocks, (c) 2004 Simtec Electronics\n");
clk_xtal.rate = xtal;
if (s3c24xx_register_clock(&clk_xtal) < 0)
printk(KERN_ERR "failed to register master xtal\n");
if (s3c24xx_register_clock(&clk_mpll) < 0)
printk(KERN_ERR "failed to register mpll clock\n");
if (s3c24xx_register_clock(&clk_upll) < 0)
printk(KERN_ERR "failed to register upll clock\n");
if (s3c24xx_register_clock(&clk_f) < 0)
printk(KERN_ERR "failed to register cpu fclk\n");
if (s3c24xx_register_clock(&clk_h) < 0)
printk(KERN_ERR "failed to register cpu hclk\n");
if (s3c24xx_register_clock(&clk_p) < 0)
printk(KERN_ERR "failed to register cpu pclk\n");
return 0;
}
--------------------------------------------------------
就如上面所说的,这个函数通过调用s3c24xx_register_clock()一一注册了六个时钟变量clk_xtal,clk_mpll,clk_upll,clk_f,clk_h,clk_p。它们定义在文件plat-s3c的clock.c中:
struct clk clk_xtal = {
.name = "xtal",
.id = -1,
.rate = 0,
.parent = NULL,
.ctrlbit = 0,
};
struct clk clk_ext = {
.name = "ext",
.id = -1,
};
struct clk clk_epll = {
.name = "epll",
.id = -1,
};
struct clk clk_mpll = {
.name = "mpll",
.id = -1,
.set_rate = clk_default_setrate,
};
struct clk clk_upll = {
.name = "upll",
.id = -1,
.parent = NULL,
.ctrlbit = 0,
};
struct clk clk_f = {
.name = "fclk",
.id = -1,
.rate = 0,
.parent = &clk_mpll,
.ctrlbit = 0,
.set_rate = clk_default_setrate,
};
struct clk clk_h = {
.name = "hclk",
.id = -1,
.rate = 0,
.parent = NULL,
.ctrlbit = 0,
.set_rate = clk_default_setrate,
};
struct clk clk_p = {
.name = "pclk",
.id = -1,
.rate = 0,
.parent = NULL,
.ctrlbit = 0,
.set_rate = clk_default_setrate,
};
struct clk结构体的定义如下:
---------------------------------------------------------------------------------------
struct clk {
struct list_head list;
struct module *owner;
struct clk *parent;
const char *name;
int id;
int usage;
unsigned long rate;
unsigned long ctrlbit;
int (*enable)(struct clk *, int enable);
int (*set_rate)(struct clk *c, unsigned long rate);
unsigned long (*get_rate)(struct clk *c);
unsigned long (*round_rate)(struct clk *c, unsigned long rate);
int (*set_parent)(struct clk *c, struct clk *parent);
};
------------------------------------------------------------------------------------------
注册代码如下:
-------------------------------------------------------------------------------------
int s3c24xx_register_clock(struct clk *clk)
{
if (clk->enable == NULL)
clk->enable = clk_null_enable;
BUG_ON(clk->list.prev != clk->list.next);
spin_lock(&clocks_lock);
list_add(&clk->list, &clocks);
spin_unlock(&clocks_lock);
return 0;
}
--------------------------------------------------------
主要看list_add(&clk->list,&clocks).系统中存在一个clocks的列表,这个就是通过list_add()把clk的list添加到这个列表中,从而注册。
s3c244x_setup_clocks()函数如下:
---------------------------------------------------------------------------
void __init_or_cpufreq s3c244x_setup_clocks(void)
{
struct clk *xtal_clk;
unsigned long clkdiv;
unsigned long camdiv;
unsigned long xtal;
unsigned long hclk, fclk, pclk;
int hdiv = 1;
xtal_clk = clk_get(NULL, "xtal");
xtal = clk_get_rate(xtal_clk);
clk_put(xtal_clk);
fclk = s3c24xx_get_pll(__raw_readl(S3C2410_MPLLCON), xtal) * 2;
clkdiv = __raw_readl(S3C2410_CLKDIVN);
camdiv = __raw_readl(S3C2440_CAMDIVN);
switch (clkdiv & S3C2440_CLKDIVN_HDIVN_MASK) {
case S3C2440_CLKDIVN_HDIVN_1:
hdiv = 1;
break;
case S3C2440_CLKDIVN_HDIVN_2:
hdiv = 2;
break;
case S3C2440_CLKDIVN_HDIVN_4_8:
hdiv = (camdiv & S3C2440_CAMDIVN_HCLK4_HALF) ? 8 : 4;
break;
case S3C2440_CLKDIVN_HDIVN_3_6:
hdiv = (camdiv & S3C2440_CAMDIVN_HCLK3_HALF) ? 6 : 3;
break;
}
hclk = fclk / hdiv;
pclk = hclk / ((clkdiv & S3C2440_CLKDIVN_PDIVN) ? 2 : 1);
printk("S3C244X: core %ld.ld MHz, memory %ld.ld MHz, peripheral %ld.ld MHz\n",
print_mhz(fclk), print_mhz(hclk), print_mhz(pclk));
s3c24xx_setup_clocks(fclk, hclk, pclk);
}
---------------------------------------------------------------------------
在这里有必要简单的说下s3c2440的时钟发生模块
主时钟源来自外部晶振(XTIpll)或者外部时钟。用芯片引脚OM[3:2]来选择,一般情况下,OM3和OM2接地,这样,主时钟源即为外部晶振(此时USB时钟源也是外部晶振)。集成电路MPLL产生一个在频率和相位上同步于参考输入信号的输出信号,计算公式为: