Linux clock子系统【4】-从CLK_OF_DECLARE 解析时钟驱动(provider侧)


前言

紧接着Linux clock子系统【3】-i2c控制器打开时钟的流程分析

  1. 分析讨论imx6ull时钟的注册流程
  2. 以及I2C时钟如何被注册,和被拿走的

前情阅读:
Linux clock子系统【2】- 一文搞懂 | Linux 时钟子系统
start_kernel(void)
time_init();

重要结构体:


一、时钟在初始化(源码分析)

1.1 CLK_OF_DECLARE

Linux系统时钟在初始化时经常用到 CLK_OF_DECLARE 这个宏,现在以 imx6ull为例做分析

“of_clk_init()”被调用的地方:

/kernel-4.4/init/main.c
asmlinkage __visible void __init start_kernel(void)
time_init();
    /kernel-4.4/arch/arm64/kernel/time.c
    void __init time_init(void)
        of_clk_init(NULL);
CLK_OF_DECLARE(imx6ul, "fsl,imx6ul-ccm", imx6ul_clocks_init);
/*CLK_OF_DECLARE 的定义:*/
#define CLK_OF_DECLARE(name, compat, fn) OF_DECLARE_1(clk, name, compat, fn)
/*OF_DECLARE_1 的定义:OF_DECLARE_1(clk,imx6ul,"fsl,imx6ul-ccm",imx6ul_clock_init)*/
typedef void (*of_init_fn_1)(struct device_node *); //函数指针 
#define OF_DECLARE_1(table, name, compat, fn) \
		_OF_DECLARE(table, name, compat, fn, of_init_fn_1)		
/*_OF_DECLARE 的定义:*/
/*
 * Struct used for matching a device
 */
struct of_device_id {
	char	name[32];
	char	type[32];
	char	compatible[128];
	const void *data;
};
 /*static const struct of_device_id __of_table_imx6ul
 		__used __section(__clk_of_table)
 		= { .compatible = "fsl,imx6ul-ccm",
 			.data = imx6ul_clock_init,
 		}
 */
#ifdef CONFIG_OF        //CONFIG_OF的定义
#define _OF_DECLARE(table, name, compat, fn, fn_type)			\
	static const struct of_device_id __of_table_##name		\
		__used __section(__##table##_of_table)			\
		 = { .compatible = compat,				\
		     .data = (fn == (fn_type)NULL) ? fn : fn  }
#else
#define _OF_DECLARE(table, name, compat, fn, fn_type)			\
	static const struct of_device_id __of_table_##name		\
		__attribute__((unused))					\
		 = { .compatible = compat,				\
		     .data = (fn == (fn_type)NULL) ? fn : fn }
#endif

/*定义了一个 struct of_device_id 的结构体变量*/
/*如果定义CONFIG_OF,就将 struct of_device_id 变量__of_table_imx6ul放到 __clk_of_table 下;*/
/*__clk_of_table 在drivers/clk/clk.c 文件下的 of_clk_init() 函数中调用:*/

/*of_clk_init() - Scan and init clock providers from the DT*/

struct clock_provider {
	of_clk_init_cb_t clk_init_cb;
	struct device_node *np;
	struct list_head node;
};
static LIST_HEAD(clk_provider_list);
	
void __init of_clk_init(const struct of_device_id *matches)
{
	const struct of_device_id *match;
	struct device_node *np;
	struct clock_provider *clk_provider, *next;
	bool is_init_done;
	bool force = false;

	if (!matches)
		matches = &__clk_of_table;
	
	/* ckil: clock@0 {
			compatible = "fixed-clock";
		};
		osc: clock@1 {
			compatible = "fixed-clock";
		};
	    ipp_di0: clock@2 {
			compatible = "fixed-clock";
		};
		ipp_di1: clock@3 {
			compatible = "fixed-clock";
		};
		clks: ccm@020c4000 {
				compatible = "fsl,imx6ul-ccm";
				reg = <0x020c4000 0x4000>;
				clocks = <&ckil>, <&osc>, <&ipp_di0>, <&ipp_di1>;
				clock-names = "ckil", "osc", "ipp_di0", "ipp_di1";
		};
	*/
	/* First prepare the list of the clocks providers */
	for_each_matching_node_and_match(np, matches, &match)
	/*
	#define for_each_matching_node_and_match(dn, matches, match) \
		for (dn = of_find_matching_node_and_match(NULL, matches, match); \
	    	 dn; dn = of_find_matching_node_and_match(dn, matches, match)) */
	  {
		struct clock_provider *parent =
			kzalloc(sizeof(struct clock_provider),	GFP_KERNEL);

		parent->clk_init_cb = match->data;
		parent->np = np;
		list_add_tail(&parent->node, &clk_provider_list);
	}
	/*开始初始化*/
	while (!list_empty(&clk_provider_list)) {
		is_init_done = false;
		list_for_each_entry_safe(clk_provider, next,
					&clk_provider_list, node) {
			if (force || parent_ready(clk_provider->np)) {
				
				/*(驱动初始化是有顺序的)
				  of_fixed_clk_setup
				  of_fixed_clk_setup
				  of_fixed_clk_setup
				  of_fixed_clk_setup
				  imx6ul_clocks_init
				*/
				clk_provider->clk_init_cb(clk_provider->np);
				of_clk_set_defaults(clk_provider->np, true);

				list_del(&clk_provider->node);
				kfree(clk_provider);
				is_init_done = true;
			}
		}


		 /*
		 *我们没有设法初始化任何
		 *在最后一次循环中保留提供商,所以现在我们
		 *无条件初始化所有剩余的元素
		 *如果时钟父节点不是强制的
		 */
		if (!is_init_done)
			force = true;
	}
}
linux/of.h(
	#define for_each_matching_node_and_match(dn, matches, match) \
	for (dn = of_find_matching_node_and_match(NULL, matches, match); \
	     dn; dn = of_find_matching_node_and_match(dn, matches, match))
	
	#define for_each_of_allnodes_from(from, dn) \
		for (dn = __of_find_all_nodes(from); dn; dn = __of_find_all_nodes(dn))
	
	struct device_node {
		const char *name;
		const char *type;
		phandle phandle;
		const char *full_name;
		struct fwnode_handle fwnode;
	
		struct	property *properties;
		struct	property *deadprops;	/* removed properties */
		struct	device_node *parent;
		struct	device_node *child;
		struct	device_node *sibling;
		struct	kobject kobj;
		unsigned long _flags;
		void	*data;
	#if defined(CONFIG_SPARC)
		const char *path_component_name;
		unsigned int unique_id;
		struct of_irq_controller *irq_trans;
	#endif
	};
)
drivers/of/base.c (
	
	/*of_find_matching_node_and_match - Find a node based on an of_device_id match table*/
	struct device_node *of_find_matching_node_and_match(struct device_node *from,
					const struct of_device_id *matches,
					const struct of_device_id **match)
	{
		struct device_node *np;
		const struct of_device_id *m;
		unsigned long flags;
	
		if (match)
			*match = NULL;/*从NULL开始搜索*/
	
		raw_spin_lock_irqsave(&devtree_lock, flags);
		for_each_of_allnodes_from(from, np) (NULL,np)
		/*
		#define for_each_of_allnodes_from(from, dn) \
			for (dn = __of_find_all_nodes(from); dn; dn = __of_find_all_nodes(dn))
		*/
		/*
			clks: ccm@020c4000 {
				compatible = "fsl,imx6ul-ccm";
				reg = <0x020c4000 0x4000>;
				interrupts = <GIC_SPI 87 IRQ_TYPE_LEVEL_HIGH>,
					     <GIC_SPI 88 IRQ_TYPE_LEVEL_HIGH>;
				#clock-cells = <1>;
				clocks = <&ckil>, <&osc>, <&ipp_di0>, <&ipp_di1>;
				clock-names = "ckil", "osc", "ipp_di0", "ipp_di1";
			};
		*/
		{
			m = __of_match_node(matches, np);(matches = &__clk_of_table)
			if (m && of_node_get(np)) {
				if (match){
					printk("%s,%s\n\n",__FUNCTION__,m->compatible);
					*match = m;
				}
				break;
			}
		}
		of_node_put(from);
		raw_spin_unlock_irqrestore(&devtree_lock, flags);
		return np;
	}
	
	struct device_node *__of_find_all_nodes(struct device_node *prev)
	{
		struct device_node *np;
		if (!prev) {
			np = of_root;
		} else if (prev->child) {
			np = prev->child;
		} else {
			/* Walk back up looking for a sibling, or the end of the structure */
			np = prev;
			while (np->parent && !np->sibling)
				np = np->parent;
			np = np->sibling; /* Might be null at the end of the tree */
		}
		return np;
	}
	/*找到一个有效node,就轮询比较一遍matches*/
	struct of_device_id matches[] = {
			[0] = {
				.compatible=fixed-clock,
				.data=of_fixed_clk_setup,
			},
			[1] = {
				.compatible=fsl,imx53-ccm,
				.data=mx53_clocks_init,
			},
			[2] = {
				.compatible=fsl,imx6ul-ccm,
				.data=imx6ul_clocks_init,
			}, 
	}
	static
	const struct of_device_id *__of_match_node(const struct of_device_id *matches,
						   const struct device_node *node)
	{
		const struct of_device_id *best_match = NULL;
		int score, best_score = 0;
	
		if (!matches)
			return NULL;
	
		for (; matches->name[0] || matches->type[0] || matches->compatible[0]; matches++) {
			score = __of_device_is_compatible(node, matches->compatible,
							  matches->type, matches->name);
			if (score > best_score) {
				best_match = matches;
				best_score = score;
			}
		}
	
		return best_match;
	}
)

1.2 imx6ul_clocks_init(Register a clock provider for a node)

依据上文Linux clock子系统【3】-i2c控制器打开时钟的流程分析
可知devm_clk_get(&pdev->dev, NULL);获取时钟的流程了,但是既然有时钟provider的获取,那么就有时钟provider的注册。

/*
	of_fixed_clk_setup
	1-2-3-of_clk_add_provider
	of_fixed_clk_setup
	1-2-3-of_clk_add_provider
	of_fixed_clk_setup
	1-2-3-of_clk_add_provider
	of_fixed_clk_setup
	1-2-3-of_clk_add_provider
	imx6ul_clocks_init
	1-2-3-of_clk_add_provider*/
/*那么,这个函数指针又是在哪被赋值的呢?*/
/*既然有遍历,就有add链表。*/
arch/arm/mach-imx/clk-imx6ul.c(
	/*ccm_node=clks: ccm@020c4000*/
	/*
		struct clk_onecell_data {
			struct clk **clks;
			unsigned int clk_num;
		};
	*/
	CLK_OF_DECLARE(imx6ul, "fsl,imx6ul-ccm", imx6ul_clocks_init);
	static struct clk *clks[IMX6UL_CLK_END];
	static void __init imx6ul_clocks_init(struct device_node *ccm_node)
	{
		static struct clk_onecell_data clk_data;
		clks[IMX6UL_CLK_I2C1]		= imx_clk_gate2("i2c1",		"perclk",	base + 0x70,	6);/*以i2c为例*/
		clks[IMX6UL_CLK_APBHDMA]	= imx_clk_gate2("apbh_dma",	"bch_podf",	base + 0x68,	4);
		clk_data.clks = clks;
		clk_data.clk_num = ARRAY_SIZE(clks);
		of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
	}
)

drivers/clk/clk.c(
		/**
	 * of_clk_add_provider() - Register a clock provider for a node
	 * @np: Device node pointer associated with clock provider
	 * @clk_src_get: callback for decoding clock
	 * @data: context pointer for @clk_src_get callback.
	 */
		struct of_clk_provider {
			struct list_head link;
		
			struct device_node *node;
			struct clk *(*get)(struct of_phandle_args *clkspec, void *data);
			void *data;
		};
		static LIST_HEAD(of_clk_providers);

	int of_clk_add_provider(struct device_node *np,
				struct clk *(*clk_src_get)(struct of_phandle_args *clkspec,void *data),
				void *data)
	{
		struct of_clk_provider *cp;
		int ret;
	
		cp = kzalloc(sizeof(struct of_clk_provider), GFP_KERNEL);/*创建一个全局 时钟链表头*/
		if (!cp)
			return -ENOMEM;
	
		cp->node = of_node_get(np);/*ccm@020c4000*/
		cp->data = data;
		cp->get = clk_src_get;
	
		mutex_lock(&of_clk_mutex);
		list_add(&cp->link, &of_clk_providers);
		mutex_unlock(&of_clk_mutex);
		pr_debug("Added clock from %s\n", np->full_name);
	
		ret = of_clk_set_defaults(np, true);
		if (ret < 0)
			of_clk_del_provider(np);
	
		return ret;
	}
	/*我们追到了这个api,这个非常关键,它将由具体平台的实现框架进行调用*/
	/*Linux clock子系统【3】-i2c控制器打开时钟的流程分析(142行)*/
	struct clk *of_clk_src_onecell_get(struct of_phandle_args *clkspec, void *data)
	{
		struct clk_onecell_data *clk_data = data;
		unsigned int idx = clkspec->args[0];
	
		if (idx >= clk_data->clk_num) {
			pr_err("%s: invalid clock index %d\n", __func__, idx);
			return ERR_PTR(-EINVAL);
		}
	
		return clk_data->clks[idx];
	}
)

1.3 imx_clk_gate2(gate类)

/*np=ccm_node=clks: ccm@020c4000*/
void __iomem *base = of_iomap(np, 0);
static const char *perclk_sels[] = { "ipg", "osc", };
struct clk *clks[IMX6UL_CLK_PERCLK_SEL] = imx_clk_mux("perclk_sel",	base + 0x1c, 6,  1, perclk_sels, ARRAY_SIZE(perclk_sels));
struct clk *clks[IMX6UL_CLK_PERCLK]		= imx_clk_divider("perclk",	   "perclk_sel",	base + 0x1c, 0,  6);
struct clk *clks[IMX6UL_CLK_I2C1]		= imx_clk_gate2("i2c1",		"perclk",	base + 0x70,	6);/*以i2c为例*/
static inline struct clk *imx_clk_gate2(const char *name, const char *parent,
		void __iomem *reg, u8 shift)
{
	return clk_register_gate2(NULL, name, parent,
			CLK_SET_RATE_PARENT | CLK_SET_RATE_GATE,
			reg, shift, 0, &imx_ccm_lock, NULL);
}

void __iomem *of_iomap(struct device_node *np, int index)
{
	struct resource res;

	if (of_address_to_resource(np, index, &res))
		return NULL;
	pr_info("%s_%x\n\n",__FUNCTION__,res.start);/*of_iomap_20c4000*/
	return ioremap(res.start, resource_size(&res));
}

总结

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值