http://blog.csdn.net/woshixingaaa/archive/2011/05/17/6426203.aspx
内核启动时,会调用s3c24xx_register_clock函数注册很多时钟,所谓注册,就是在一个链表中保存各种"struct clk*"结构指针,这些"struct clk"结构有:clk_f(表示FCLK),clk_h(表示HCLK),clk_p(表示PCLK)等。然后可以通过clk_get_rate函数获得获得某类时钟频率。下面到内核中分析一下源码,这里跟踪内核启动时clock system的初始化过程:
- asmlinkage void __init start_kernel(void)
- {
- ............
- setup_arch(&command_line);
- ............
- }
start_kernel调用了setup_arch(&command_line):
- void __init setup_arch(char **cmdline_p)
- {
- ..........
- paging_init(mdesc);
- ..........
- }
setup_arch调用了paging_init(mdesc):
- void __init paging_init(struct machine_desc *mdesc){
- .............
- devicemaps_init(mdesc);
- .............
- }
paging_init调用了devicemaps_init(mdesc):
- static void __init devicemaps_init(struct machine_desc *mdesc)
- {
- ....................
-
- if (mdesc->map_io)
- mdesc->map_io();
- ...................
- }
在我们板子的文件中查到这个map_io函数:
- static void __init smdk2440_map_io(void)
- {
- s3c24xx_init_io(smdk2440_iodesc, ARRAY_SIZE(smdk2440_iodesc));
- s3c24xx_init_clocks(12000000);
- s3c24xx_init_uarts(smdk2440_uartcfgs, ARRAY_SIZE(smdk2440_uartcfgs));
- }
这个函数有一句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);
- }
这个函数是设置晶振的频率为12M,也就是我板子上的晶振12M。注意这里最后一句:(cpu->init_clocks)(xtal);我们要查看cpu_table了。
- static struct cpu_table cpu_ids[] __initdata = {
- ......................
- {
- .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
- },
- .....................
- };
在cpu_table中可以找到这个init_clocks函数,也就是我们苦苦寻觅的clock system初始化函数了,真是众里寻他千百度,那人却在灯火阑珊处。
- void __init s3c244x_init_clocks(int xtal)
- {
-
-
-
-
- s3c24xx_register_baseclocks(xtal);
- s3c244x_setup_clocks();
- s3c2410_baseclk_add();
- }
这个s3c244x_init_clocks完成了clock system全部的初始化工作。现在一个一个来分析里边的3个函数。s3c24xx_register_baseclocks()函数在arch/arm/plat-s3c/clock.c中实现如下:这里对基本的时钟clk_xtal,clk_mpll,clk_upll,clk_f,clk_h,clk_p进行了注册。
- 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;
- }
下边看一下这个注册函数,主要任务就是把struct clk结构添加到clocks链表中。
static LIST_HEAD(clocks);
这个是链表的头的注册函数。注册的struct clk结构体都要添加到这个clocks链表中。
- int s3c24xx_register_clock(struct clk *clk)
- {
- .........
- list_add(&clk->list, &clocks);
- .........
- }
现在来看第二个函数:它的任务就是设置fclk,hclk,pclk,相信如果认真写过arm裸机程序的人一定很容易看懂下边的代码了,可以对照s3c2440的手册来看的。
- void __init_or_cpufreq s3c244x_setup_clocks(void)
- {
- .................
- s3c24xx_setup_clocks(fclk, hclk, pclk);
- }
这里调用了一个s3c24xx_setup_clocks函数,下面看它的实现:
-
- void __init_or_cpufreq s3c24xx_setup_clocks(unsigned long fclk,
- unsigned long hclk,
- unsigned long pclk)
- {
- clk_upll.rate = s3c24xx_get_pll(__raw_readl(S3C2410_UPLLCON),
- clk_xtal.rate);
-
- clk_mpll.rate = fclk;
- clk_h.rate = hclk;
- clk_p.rate = pclk;
- clk_f.rate = fclk;
- }
就是把得到的fclk,hclk,pclk赋值相应结构体。下面来看第三个函数,这个主要就是对外设的struct clk进行注册。这个函数一共分两部分,有两个数组,一个是init_clocks,也就是在boot时需要提供时钟的,一个是init_clocks_disable,这里的每个成员都是在boot的时候需要disable时钟的。这两个数组分别进行注册,但是注册init_clocks_disable数组中成员的for循环中调用了s3c2410_clkcon_enable(clkp, 0);也就是将相应的clk disable掉。
- int __init s3c2410_baseclk_add(void)
- {
- unsigned long clkslow = __raw_readl(S3C2410_CLKSLOW);
- unsigned long clkcon = __raw_readl(S3C2410_CLKCON);
- struct clk *clkp;
- struct clk *xtal;
- int ret;
- int ptr;
-
- clk_upll.enable = s3c2410_upll_enable;
-
- if (s3c24xx_register_clock(&clk_usb_bus) < 0)
- printk(KERN_ERR "failed to register usb bus clock/n");
-
-
-
- clkp = init_clocks;
- for (ptr = 0; ptr < ARRAY_SIZE(init_clocks); ptr++, clkp++) {
-
-
- clkp->usage = clkcon & clkp->ctrlbit ? 1 : 0;
-
- ret = s3c24xx_register_clock(clkp);
- if (ret < 0) {
- printk(KERN_ERR "Failed to register clock %s (%d)/n",
- clkp->name, ret);
- }
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
- clkp = init_clocks_disable;
- for (ptr = 0; ptr < ARRAY_SIZE(init_clocks_disable); ptr++, clkp++) {
-
- ret = s3c24xx_register_clock(clkp);
- if (ret < 0) {
- printk(KERN_ERR "Failed to register clock %s (%d)/n",
- clkp->name, ret);
- }
-
- s3c2410_clkcon_enable(clkp, 0);
- }
-
-
-
- xtal = clk_get(NULL, "xtal");
-
- printk("CLOCK: Slow mode (%ld.%ld MHz), %s, MPLL %s, UPLL %s/n",
- print_mhz(clk_get_rate(xtal) /
- ( 2 * S3C2410_CLKSLOW_GET_SLOWVAL(clkslow))),
- (clkslow & S3C2410_CLKSLOW_SLOW) ? "slow" : "fast",
- (clkslow & S3C2410_CLKSLOW_MPLL_OFF) ? "off" : "on",
- (clkslow & S3C2410_CLKSLOW_UCLK_OFF) ? "off" : "on");
-
- s3c_pwmclk_init();
- return 0;
- }
在arch/arm/plat-s3c/clock.c中实现了clock system对外提供的接口:
-
- struct clk *clk_get(struct device *dev, const char *id);
-
- void clk_put(struct clk *clk);
-
- int clk_enable(struct clk *clk);
-
- void clk_disable(struct clk *clk);
-
- unsigned long clk_get_rate(struct clk *clk);
-
- int clk_set_rate(struct clk *clk, unsigned long rate);
-
- struct clk *clk_get_parent(struct clk *clk);
-
- int clk_set_parent(struct clk *clk, struct clk *parent);
阅读(1190) | 评论(0) | 转发(0) |