linux时钟管理clk_get函数分析

硬件资源越来越庞大和复杂,内核的另一个挑战就是要便捷的管理这些资源。同时,面对如此之多的平台不同的CPU

,管理机制需要统一适用,这就需要对资源的管理抽象到更加通用的层次。CPU中各个模块都需要时钟驱动,内核需

要一种机制能通用所有的平台,方便的管理CPU上所有的clk资源。这里分析Linux对clk的管理。


通常操作为以下几步:

1.定义struct clk *clk;

2.获取需要操作的clock结构体 clk=clk_get(&pdev->dev, "pclk"); /* 第一个参数一般取NULL */

3.设置频率 clk_get_rate(clk); /* 返回时钟频率 */

4.产生时钟 clk_enable(clk);

5.停止时钟clk_disable(clk);


我们下面重点分析clk_get这个函数。

clk_get函数定义在Clkdev.c (kernel_2.6.35.7\arch\arm\common) 文件中,内容如下:


struct clk *clk_get(struct device *dev, const char *con_id)
{
	const char *dev_id = dev ? dev_name(dev) : NULL;

	return clk_get_sys(dev_id, con_id);
}

这个函数有两个参数,struct device *dev这个结构非常复杂,一般我们设置成NULL

第二个人参数是我们需要设置硬件上的那部分时钟,比如adc,iis,dma等。


clk_get里调用了clk_get_sys这个函数,第一个参数我们开始设置成NULL了,

第二个参数是我们需要设置硬件相关时钟的名字。


struct clk *clk_get_sys(const char *dev_id, const char *con_id)
{
	struct clk *clk;

	mutex_lock(&clocks_mutex);
	clk = clk_find(dev_id, con_id);
	if (clk && !__clk_get(clk))
		clk = NULL;
	mutex_unlock(&clocks_mutex);

	return clk ? clk : ERR_PTR(-ENOENT);
}

clk_get_sys里面通过clk_find函数来查找我们传入的硬件名称,并返回clk类型的一个指针,

clk_find函数里面就是我们最终需要查看的内容。


static struct clk *clk_find(const char *dev_id, const char *con_id)
{
	struct clk_lookup *p;
	struct clk *clk = NULL;
	int match, best = 0;

	list_for_each_entry(p, &clocks, node) {
		match = 0;
		if (p->dev_id) {
			if (!dev_id || strcmp(p->dev_id, dev_id))
				continue;
			match += 2;
		}
		if (p->con_id) {
			if (!con_id || strcmp(p->con_id, con_id))
				continue;
			match += 1;
		}

		if (match > best) {
			clk = p->clk;
			if (match != 3)
				best = match;
			else
				break;
		}
	}
	return clk;
}

list_for_each_entry函数从clocks的链表中的表头,它受clocks_lock保护,开始查找和我们传入的硬件名称相比较,

如果找到了就返回一个指向该硬件clk类型的指针。


clk_get函数到此为止分析完毕,这里补充一点,那就是clk_get的第二个参数在哪里定义的呢,这里我的内核版本是

Linux-2.6.35.7,定义在kernel_2.6.35.7\arch\arm\mach-s5pv210\clock.c中,内容如下:


static struct clk init_clocks[] = {
	{
		.name		= "watchdog",
		.id		= -1,
		.parent		= &clk_pclk_psys.clk,
		.enable		= s5pv210_clk_ip3_ctrl,
		.ctrlbit	= (1<<22),
	}, {
		.name		= "hclk_imem",
		.id		= -1,
		.parent		= &clk_hclk_msys.clk,
		.ctrlbit	= (1 << 5),
		.enable		= s5pv210_clk_ip0_ctrl,
		.ops		= &clk_hclk_imem_ops,
	}, {
		.name		= "lcd",
		.id		= -1,
		.parent		= &clk_hclk_dsys.clk,
		.enable		= s5pv210_clk_ip1_ctrl,
		.ctrlbit	= (1<<0),
	}, {
		.name		= "uart",
		.id		= 0,
		.parent		= &clk_pclk_psys.clk,
		.enable		= s5pv210_clk_ip3_ctrl,
		.ctrlbit	= (1 << 17),
	}, {
		.name		= "uart",
		.id		= 1,
		.parent		= &clk_pclk_psys.clk,
		.enable		= s5pv210_clk_ip3_ctrl,
		.ctrlbit	= (1 << 18),
	}, {
		.name		= "uart",
		.id		= 2,
		.parent		= &clk_pclk_psys.clk,
		.enable		= s5pv210_clk_ip3_ctrl,
		.ctrlbit	= (1 << 19),
	}, {
		.name		= "uart",
		.id		= 3,
		.parent		= &clk_pclk_psys.clk,
		.enable		= s5pv210_clk_ip3_ctrl,
		.ctrlbit	= (1 << 20),
	}, {
		.name		= "i2s_v50",
		.id		= 0,
		.parent		= &clk_p,
		.enable		= s5pv210_clk_ip3_ctrl,
		.ctrlbit	= S5P_CLKGATE_IP3_I2S0 | S5P_CLKGATE_IP3_PCM0,
	}, {
		.name		= "i2s_v50",
		.id		= 1,
		.parent		= &clk_p,
		.enable		= s5pv210_clk_ip3_ctrl,
		.ctrlbit	= S5P_CLKGATE_IP3_I2S1 | S5P_CLKGATE_IP3_PCM1,
	}, {
		.name		= "i2s_v50",
		.id		= 2,
		.parent		= &clk_p,
		.enable		= s5pv210_clk_ip3_ctrl,
		.ctrlbit	= S5P_CLKGATE_IP3_I2S2 | S5P_CLKGATE_IP3_PCM2,
	}, 	{
		.name		= "clk_out",
		.id		= -1,
		.ops		= &s5pc11x_clkout_ops,
	},
};




你可能感兴趣的:(linux时钟管理clk_get函数分析)