Linux驱动修炼之道-clock框架

努力成为linux kernel hacker的人李万鹏原创作品,为梦而战。转载请标明出处

http://blog.youkuaiyun.com/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) { .................... /*这里调用具体体系结构的map_io函数*/ 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);跟踪进去:

/* s3c24xx_init_clocks * * Initialise the clock subsystem and associated information from the * given master crystal value. * * xtal = 0 -> use default PLL crystal value (normally 12MHz) * != 0 -> PLL crystal value in Hz */ 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) { /* initialise the clocks here, to allow other things like the * console to use them, and to add new ones after the initialisation */ 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; /* register our clocks */ 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链表中。

/* clock information */ static LIST_HEAD(clocks);

这个是链表的头的注册函数。注册的struct clk结构体都要添加到这个clocks链表中。

/* initialise the clock system */ 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函数,下面看它的实现:

/* initalise all the 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"); /* register clocks from clock array */ clkp = init_clocks; for (ptr = 0; ptr < ARRAY_SIZE(init_clocks); ptr++, clkp++) { /* ensure that we note the clock state */ 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); } } /* We must be careful disabling the clocks we are not intending to * be using at boot time, as subsystems such as the LCD which do * their own DMA requests to the bus can cause the system to lockup * if they where in the middle of requesting bus access. * * Disabling the LCD clock if the LCD is active is very dangerous, * and therefore the bootloader should be careful to not enable * the LCD clock if it is not needed. */ /* install (and disable) the clocks we do not need immediately */ 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); } /* show the clock-slow value */ 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); /*使能某个模块的时钟,比如ADC模块等*/ int clk_enable(struct clk *clk); /*禁止模块的时钟*/ void clk_disable(struct clk *clk); /*获得某类时钟频率*/ unsigned long clk_get_rate(struct clk *clk); /*设置某类部件的时钟(比如设置CAM接口时钟)*/ int clk_set_rate(struct clk *clk, unsigned long rate); /*获得父clk*/ struct clk *clk_get_parent(struct clk *clk); /*设置父clk*/ int clk_set_parent(struct clk *clk, struct clk *parent);

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值