我是靠谱客的博主 欢呼冬瓜,最近开发中收集的这篇文章主要介绍Linux clock子系统【4】-从CLK_OF_DECLARE 解析时钟驱动(provider侧)前言一、时钟在初始化(源码分析)总结,觉得挺不错的,现在分享给大家,希望可以做个参考。
概述
文章目录
- 前言
- 一、时钟在初始化(源码分析)
- 1.1 CLK_OF_DECLARE
- 1.2 imx6ul_clocks_init(Register a clock provider for a node)
- 1.3 imx_clk_gate2(gate类)
- 总结
前言
紧接着Linux clock子系统【3】-i2c控制器打开时钟的流程分析,
- 分析讨论
imx6ull
时钟的注册流程 - 以及
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,%snn",__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 %sn", 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 %dn", __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_%xnn",__FUNCTION__,res.start);/*of_iomap_20c4000*/
return ioremap(res.start, resource_size(&res));
}
总结
最后
以上就是欢呼冬瓜为你收集整理的Linux clock子系统【4】-从CLK_OF_DECLARE 解析时钟驱动(provider侧)前言一、时钟在初始化(源码分析)总结的全部内容,希望文章能够帮你解决Linux clock子系统【4】-从CLK_OF_DECLARE 解析时钟驱动(provider侧)前言一、时钟在初始化(源码分析)总结所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复