从零开始之驱动发开、linux驱动(三十九、Linux common clock framework(4)_总结)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_16777851/article/details/84348051

前面三节参考蜗窝大神的文章分析了Linux common clock framework的主要实现细节,本篇则是对前三篇从全局的一个整合说明。

common clock framework主要维护着四条链表

static HLIST_HEAD(clk_root_list);
static HLIST_HEAD(clk_orphan_list);
static LIST_HEAD(clk_notifier_list);
static LIST_HEAD(clocks);

其中clock链表我们在上一节的末尾已经说明,这个主要就是为了方便查找。

clk_root_list是存放根时钟的,一个链表,通常根时钟都是固定频率的时钟。(fix)

clk_orphan_list是存放孤儿节点的时钟,即一般而言没有父时钟,也不是根时钟,这种时钟通常就是孤儿时钟。

clk_notifier_list是通知链表,即某个父时钟跟新了频率,其也要通知子时钟更新它的频率计算。

注册时的位置如下

clk_register
    __clk_init

正常情况下,绝大多数时钟都是有父时钟的。一般都是root链表上某个根时钟的子时钟。(比如下图的蓝色框标注的)

当然,上面的各个root的子时钟,下面有可以继续级联子时钟。

但是对于某些特殊情况下就会出现孤儿时钟:

即注册时,其父节点还没注册的非根时钟都是孤儿时钟。orphan时钟和root时钟的管理是一致的,孤儿时钟下面,也是可以挂接子时钟的。

一般在注册时,如果子时钟先于父时钟注册,这时就会挂接到clk_orphan_list时钟链表上,这种情况下,待父时钟注册好后,子时钟又要从orphan时钟链表上移到它的父时钟下面(位于root链表下)。

函数同样位于注册时钟的函数中。

clk_register
    __clk_init

最后,再次列出注册用的哪几种函数原型。

开关类型的时钟

struct clk *clk_register_gate(struct device *dev, const char *name,
		const char *parent_name, unsigned long flags,
		void __iomem *reg, u8 bit_idx,
		u8 clk_gate_flags, spinlock_t *lock);

固定频率的时钟

struct clk *clk_register_fixed_rate(struct device *dev, const char *name,
		const char *parent_name, unsigned long flags,
		unsigned long fixed_rate);

多选一类型的时钟

void __init samsung_clk_register_mux(struct samsung_clk_provider *ctx,
				struct samsung_mux_clock *list,
				unsigned int nr_clk);

分频类型的时钟

struct clk *clk_register_divider_table(struct device *dev, const char *name,
		const char *parent_name, unsigned long flags,
		void __iomem *reg, u8 shift, u8 width,
		u8 clk_divider_flags, const struct clk_div_table *table,
		spinlock_t *lock);

综合类型的时钟(上面几个的结合,但一般用的不多)

struct clk *clk_register_composite(struct device *dev, const char *name,
			const char **parent_names, int num_parents,
			struct clk_hw *mux_hw, const struct clk_ops *mux_ops,
			struct clk_hw *rate_hw, const struct clk_ops *rate_ops,
			struct clk_hw *gate_hw, const struct clk_ops *gate_ops,
			unsigned long flags)

通过上面可以发现,倍频的时钟注册是没有的。

一般都是用的锁相环PLL(phase locked loop)来实现的。

因为上面几个标准的时钟注册函数是没有这个的(因为这个各厂商的差异太大),所以这个的ops等都是厂商单独来实现的。

已三星为例,三星是根据锁相环电路(芯片)的不通过来实现自己平台下的各种处理器的pll注册的。


static void __init _samsung_clk_register_pll(struct samsung_clk_provider *ctx,
				const struct samsung_pll_clock *pll_clk,
				void __iomem *base)
{
	struct samsung_clk_pll *pll;
	struct clk *clk;
	struct clk_init_data init;
	int ret, len;

	pll = kzalloc(sizeof(*pll), GFP_KERNEL);
	if (!pll) {
		pr_err("%s: could not allocate pll clk %s\n",
			__func__, pll_clk->name);
		return;
	}

	init.name = pll_clk->name;
	init.flags = pll_clk->flags;
	init.parent_names = &pll_clk->parent_name;
	init.num_parents = 1;

	if (pll_clk->rate_table) {
		/* find count of rates in rate_table */
		for (len = 0; pll_clk->rate_table[len].rate != 0; )
			len++;

		pll->rate_count = len;
		pll->rate_table = kmemdup(pll_clk->rate_table,
					pll->rate_count *
					sizeof(struct samsung_pll_rate_table),
					GFP_KERNEL);
		WARN(!pll->rate_table,
			"%s: could not allocate rate table for %s\n",
			__func__, pll_clk->name);
	}

	switch (pll_clk->type) {
	case pll_2126:
		init.ops = &samsung_pll2126_clk_ops;
		break;
	case pll_3000:
		init.ops = &samsung_pll3000_clk_ops;
		break;
	/* clk_ops for 35xx and 2550 are similar */
	case pll_35xx:
	case pll_2550:
	case pll_1450x:
	case pll_1451x:
	case pll_1452x:
		if (!pll->rate_table)
			init.ops = &samsung_pll35xx_clk_min_ops;
		else
			init.ops = &samsung_pll35xx_clk_ops;
		break;
	case pll_4500:
		init.ops = &samsung_pll45xx_clk_min_ops;
		break;
	case pll_4502:
	case pll_4508:
		if (!pll->rate_table)
			init.ops = &samsung_pll45xx_clk_min_ops;
		else
			init.ops = &samsung_pll45xx_clk_ops;
		break;
	/* clk_ops for 36xx and 2650 are similar */
	case pll_36xx:
	case pll_2650:
		if (!pll->rate_table)
			init.ops = &samsung_pll36xx_clk_min_ops;
		else
			init.ops = &samsung_pll36xx_clk_ops;
		break;
	case pll_6552:
	case pll_6552_s3c2416:
		init.ops = &samsung_pll6552_clk_ops;
		break;
	case pll_6553:
		init.ops = &samsung_pll6553_clk_ops;
		break;
	case pll_4600:
	case pll_4650:
	case pll_4650c:
	case pll_1460x:
		if (!pll->rate_table)
			init.ops = &samsung_pll46xx_clk_min_ops;
		else
			init.ops = &samsung_pll46xx_clk_ops;
		break;
	case pll_s3c2410_mpll:
		if (!pll->rate_table)
			init.ops = &samsung_s3c2410_mpll_clk_min_ops;
		else
			init.ops = &samsung_s3c2410_mpll_clk_ops;
		break;
	case pll_s3c2410_upll:
		if (!pll->rate_table)
			init.ops = &samsung_s3c2410_upll_clk_min_ops;
		else
			init.ops = &samsung_s3c2410_upll_clk_ops;
		break;
	case pll_s3c2440_mpll:
		if (!pll->rate_table)
			init.ops = &samsung_s3c2440_mpll_clk_min_ops;
		else
			init.ops = &samsung_s3c2440_mpll_clk_ops;
		break;
	case pll_2550x:
		init.ops = &samsung_pll2550x_clk_ops;
		break;
	case pll_2550xx:
		if (!pll->rate_table)
			init.ops = &samsung_pll2550xx_clk_min_ops;
		else
			init.ops = &samsung_pll2550xx_clk_ops;
		break;
	case pll_2650x:
		if (!pll->rate_table)
			init.ops = &samsung_pll2650x_clk_min_ops;
		else
			init.ops = &samsung_pll2650x_clk_ops;
		break;
	case pll_2650xx:
		if (!pll->rate_table)
			init.ops = &samsung_pll2650xx_clk_min_ops;
		else
			init.ops = &samsung_pll2650xx_clk_ops;
		break;
	default:
		pr_warn("%s: Unknown pll type for pll clk %s\n",
			__func__, pll_clk->name);
	}

	pll->hw.init = &init;
	pll->type = pll_clk->type;
	pll->lock_reg = base + pll_clk->lock_offset;
	pll->con_reg = base + pll_clk->con_offset;

	clk = clk_register(NULL, &pll->hw);
	if (IS_ERR(clk)) {
		pr_err("%s: failed to register pll clock %s : %ld\n",
			__func__, pll_clk->name, PTR_ERR(clk));
		kfree(pll);
		return;
	}

	samsung_clk_add_lookup(ctx, clk, pll_clk->id);

	if (!pll_clk->alias)
		return;

	ret = clk_register_clkdev(clk, pll_clk->alias, pll_clk->dev_name);
	if (ret)
		pr_err("%s: failed to register lookup for %s : %d",
			__func__, pll_clk->name, ret);
}

void __init samsung_clk_register_pll(struct samsung_clk_provider *ctx,
			const struct samsung_pll_clock *pll_list,
			unsigned int nr_pll, void __iomem *base)
{
	int cnt;

	for (cnt = 0; cnt < nr_pll; cnt++)
		_samsung_clk_register_pll(ctx, &pll_list[cnt], base);
}

下面列出三星目前有的锁相环类型

enum samsung_pll_type {
	pll_2126,
	pll_3000,
	pll_35xx,
	pll_36xx,
	pll_2550,
	pll_2650,
	pll_4500,
	pll_4502,
	pll_4508,
	pll_4600,
	pll_4650,
	pll_4650c,
	pll_6552,
	pll_6552_s3c2416,
	pll_6553,
	pll_s3c2410_mpll,
	pll_s3c2410_upll,
	pll_s3c2440_mpll,
	pll_2550x,
	pll_2550xx,
	pll_2650x,
	pll_2650xx,
	pll_1450x,
	pll_1451x,
	pll_1452x,
	pll_1460x,
};

因为每种锁相环支持的操作不一样,所以三星对锁相环的有多种ops接口。

最后就是每种时钟注册时为了方便查找,都统一绑定在clock链表的注册接口:

int clk_register_clkdev(struct clk *clk, const char *con_id,
	const char *dev_id);

即最终,一般情况下下

猜你喜欢

转载自blog.csdn.net/qq_16777851/article/details/84348051