Linux CLK驱动笔记

数据结构

在这里插入图片描述
  copy的(https://blog.csdn.net/melody157398/article/details/122206597)

Controller

__clk_register

#define clk_hw_register_fixed_rate(dev, name, parent_name, flags, fixed_rate)  \
	__clk_hw_register_fixed_rate((dev), NULL, (name), (parent_name), NULL, \
				     NULL, (flags), (fixed_rate), 0, 0)

struct clk_hw *__clk_hw_register_fixed_rate(struct device *dev,
		struct device_node *np, const char *name,
		const char *parent_name, const struct clk_hw *parent_hw,
		const struct clk_parent_data *parent_data, unsigned long flags,
		unsigned long fixed_rate, unsigned long fixed_accuracy,
		unsigned long clk_fixed_flags)
{
	struct clk_fixed_rate *fixed;
	struct clk_hw *hw;
	struct clk_init_data init = {};
	int ret = -EINVAL;

	/* allocate fixed-rate clock */
	fixed = kzalloc(sizeof(*fixed), GFP_KERNEL);
	if (!fixed)
		return ERR_PTR(-ENOMEM);

	init.name = name;
	init.ops = &clk_fixed_rate_ops;
	init.flags = flags;
	init.parent_names = parent_name ? &parent_name : NULL;
	init.parent_hws = parent_hw ? &parent_hw : NULL;
	init.parent_data = parent_data;
	if (parent_name || parent_hw || parent_data)
		init.num_parents = 1;
	else
		init.num_parents = 0;

	/* struct clk_fixed_rate assignments */
	fixed->flags = clk_fixed_flags;
	fixed->fixed_rate = fixed_rate;
	fixed->fixed_accuracy = fixed_accuracy;
	fixed->hw.init = &init;

	/* register the clock */
	hw = &fixed->hw;
	if (dev || !np)
		ret = clk_hw_register(dev, hw);
	else if (np)
		ret = of_clk_hw_register(np, hw);
	if (ret) {
		kfree(fixed);
		hw = ERR_PTR(ret);
	}

	return hw;
}

  传入的device_node、parent_name为空,只有name和rate,初始化clk_hw,未传入device_node,最终还是调用的clk_hw_register,其中ops为通用的clk_fixed_rate_ops,只有recalc_rate函数的实现。

int devm_clk_hw_register(struct device *dev, struct clk_hw *hw)
{
    ...
	ret = clk_hw_register(dev, hw);
    ...
}
int clk_hw_register(struct device *dev, struct clk_hw *hw)
{
	return PTR_ERR_OR_ZERO(__clk_register(dev, dev_or_parent_of_node(dev),
			       hw));
}
static struct clk *
__clk_register(struct device *dev, struct device_node *np, struct clk_hw *hw)
{
    ...
    hw->init = NULL;
    core->name = kstrdup_const(init->name, GFP_KERNEL);
    core->ops = init->ops;
    ret = clk_core_populate_parent_map(core, init);
	INIT_HLIST_HEAD(&core->clks);
    clk_core_link_consumer(core, hw->clk);
    __clk_core_init
    ...
}
static int __clk_core_init(struct clk_core *core)
{
    ...
    if (clk_core_lookup(core->name)){

    }
    if (core->ops->init) {
		ret = core->ops->init(core->hw);
	}
    parent = core->parent = __clk_init_parent(core);
    {
        if (core->num_parents > 1 && core->ops->get_parent)
		    index = core->ops->get_parent(core->hw);
	    return clk_core_get_parent_by_index(core, index);
			-->clk_core_fill_parent_index(core, index);
			return core->parents[index].core;

    }

    if (parent) {
		hlist_add_head(&core->child_node, &parent->children);
		core->orphan = parent->orphan;
	} else if (!core->num_parents) {
		hlist_add_head(&core->child_node, &clk_root_list);
		core->orphan = false;
	} else {
		hlist_add_head(&core->child_node, &clk_orphan_list);
		core->orphan = true;
	}

    if (core->ops->recalc_accuracy)
		core->accuracy = core->ops->recalc_accuracy(core->hw,
					clk_core_get_accuracy_no_lock(parent));
	else if (parent)
		core->accuracy = parent->accuracy;
	else
		core->accuracy = 0;
    
    clk_core_reparent_orphans_nolock
    ...
}

  clk_register首先初始化clk_ops,clk_core_populate_parent_map初这个函数主要是根据parent_num申请了clk_parent_map,则将parent_names[i]字符串赋值给了clk_parent_map的name。

  初始化core->clks链表,然后clk_core_link_consumer将hw->clk->clk_node作为core->clks链表的头

  __clk_core_init先检查是否有相同的同名函数已经注册,然后检查一些函数接口的完整性,有init函数先执行init函数,然后__clk_init_parent得到parent,这里执行get_parent来读取寄存器得到index,如果parent已经被初始化过了,则将其插入到parent->children链表,没有初始化且没有父时钟插入到root链表,否则插入到orphan链表。

  有recalc_accuracy函数则执行该函数计算时钟准确率,在默写形况下可能对选择父时钟有用。然后执行recalc_rate获取时钟速率
  clk的parent是在__clk_init_parent的时候被填充的,没有全部填充,先执行get_parent获取index,然后执行clk_core_fill_parent_index填充当前的parent

  某些时钟最好需要有CLK_IS_CRITICAL的flag,这些关键时钟表示需要一直是工作的,例如cpu的时钟登,有该标志位会执行clk_core_prepare和clk_core_enable,不会被后late_initcall调用clk_disable_unused给关掉

  clk_core_reparent_orphans_nolock函数遍历orphan链表(没看明白)

of_clk_add_hw_provider

devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, hw_clks);
...
int devm_of_clk_add_hw_provider(struct device *dev,
			struct clk_hw *(*get)(struct of_phandle_args *clkspec,
					      void *data),
			void *data)
{
	...
    np = get_clk_provider_node(dev);
    ret = of_clk_add_hw_provider(np, get, data);
    ...
}

int of_clk_add_hw_provider(struct device_node *np,
			   struct clk_hw *(*get)(struct of_phandle_args *clkspec,
						 void *data),
			   void *data)
{
    ...
    struct of_clk_provider *cp;
    cp->node = of_node_get(np);
	cp->data = data;
	cp->get_hw = get;
    list_add(&cp->link, &of_clk_providers);
	clk_core_reparent_orphans();
    ...
}

  get函数和void *data为回调函数,会在consumer执行get_clk的时候调用
  当前device如果没有"#clock-cells"属性则寻找父节点,获取device_node

  创建一个clk_provider,设置他的data为传入的hw_clks,get_hw为of_clk_hw_onecell_get函数,将其加入到clk_provider链表。

Consumer

devm_clk_get

struct clk *devm_clk_get(struct device *dev, const char *id)
{
    ...
    clk = clk_get(dev, id);
    ...
}

struct clk *clk_get(struct device *dev, const char *con_id)
-->of_clk_get_hw(dev->of_node, 0, con_id);
    {
        ret = of_parse_clkspec(np, index, con_id, &clkspec);
        hw = of_clk_get_hw_from_clkspec(&clkspec);
        -->hw = __of_clk_get_hw_from_provider(provider, clkspec);
            -->return provider->get_hw(clkspec, provider->data);
    }

  devm_clk_get通过传入的id找到clock-names指向的父节点,在provider链表中查找和父节点相同的node,然后执行get_hw,get_hw为provider注册时指定的of_clk_hw_onecell_get

struct clk_hw *
of_clk_hw_onecell_get(struct of_phandle_args *clkspec, void *data)
{
	struct clk_hw_onecell_data *hw_data = data;
	unsigned int idx = clkspec->args[0];

	if (idx >= hw_data->num) {
		pr_err("%s: invalid index %u\n", __func__, idx);
		return ERR_PTR(-EINVAL);
	}

	return hw_data->hws[idx];
}

  clk_hw_onecell_data在controller初始化

clk_prepare_enable

static inline int clk_prepare_enable(struct clk *clk)
{
	int ret;

	ret = clk_prepare(clk);
	if (ret)
		return ret;
	ret = clk_enable(clk);
	if (ret)
		clk_unprepare(clk);

	return ret;
}
clk_prepare(clk)
-->clk_core_prepare_lock
    -->clk_core_prepare
    {
        if (core->prepare_count == 0)
        {
            ret = clk_core_prepare(core->parent);
            if (core->ops->prepare)
			    ret = core->ops->prepare(core->hw);
        }
        core->prepare_count++;
    }

clk_enable
-->clk_core_enable_lock
    -->clk_core_enable
       {
            if (core->enable_count == 0)
            {
                ret = clk_core_enable(core->parent);      
		        if (core->ops->enable)
			        ret = core->ops->enable(core->hw);
            }
            core->enable_count++;
       }

  clk_prepare、clk_enable的逻辑差不多,core->enable_count==0的时候,先要递归父节点都执行enable,然后在core->enable_count++,也就是说存在一个子节点enable则父节点count++,子节点disable的时候父节点count–,如果为0了则代表子节点全部disable,父节点也执行disable。

clk_disable_unprepare

  clk_disable_unprepare逻辑和clk_prepare_enable相似

clk_get_rate

clk_get_rate
-->clk_core_get_rate_recalc
    {
        if (core && (core->flags & CLK_GET_RATE_NOCACHE))
		    __clk_recalc_rates(core, 0);
	    return clk_core_get_rate_nolock(core);
    }

  如果没有CLK_GET_RATE_NOCACHE的flag则执行core->ops->recalc_rate(core->hw, parent_rate)重新获取rate

clk_set_rate

clk_set_rate
-->clk_core_set_rate_nolock
	-->clk_core_req_round_rate_nolock
		-->clk_core_round_rate_nolock
			-->clk_core_determine_round_nolock
				-->core->ops->determine_rate(core->hw, req);
				-->core->ops->round_rate(core->hw, req->rate,&req->best_parent_rate)
					return req.rate
				-->有determine_rate优先执行,没有则执行round_rate
-->clk_calc_new_rates
	-->round和determine接口有点不同,determine可能会改变parent
	-->clk_core_determine_round_nolock(core, &req);
	-->如果执行了determine_rate,req除了rate外还有best_parent
	-->clk_calc_subtree(core,new_rate,parent,p_index)
		-->根据前面的代码,parent可能为null,或者为best_parent
		-->hlist_for_each_entry(child, &core->children, child_node)
			clk_recalc
			-->core->ops->recalc_rate(core->hw, parent_rate)
-->clk_change_rate
	-->看parent是否改变,改变执行set_rate_and_parent
	-->core->ops->set_parent(core->hw, core->new_parent_index)
	-->core->ops->set_rate
	-->hlist_for_each_entry_safe(child, tmp, &core->children, child_node)
		clk_change_rate  递归执行set_rate

  clk_set_rate先执行round_rate,然后重新计算子节点的时钟,然后当前节点和所有子节点执行set_rate,其中要注意parent是否发送改变。以上是没有determine_rate只有round_rate的情况。round_rate同过标志位选择是否改变父时钟频率,而determine_rate是在多个父时钟的情况下选择最接近的父时钟

__clk_mux_determine_rate

const struct clk_ops mt_mux_ops{
	...
	.determine_rate = __clk_mux_determine_rate,
	...
};
__clk_mux_determine_rate
-->clk_mux_determine_rate_flags(hw, req, 0)
	-->for (i = 0; i < num_parents; i++){
		parent = clk_core_get_parent_by_index(core, i);
		parent_req.rate = clk_core_get_rate_nolock(parent);
	}
	-->遍历parent,得到req->best_parent_hw和req->best_parent_rate
		return 0 success

  determine_rate这个接口要注意,和parent有关,会在如下clk_round_rate、clk_core_req_round_rate_nolock中被调用。clk_core_get_parent_by_index如何index的parent为空会从clk树中检索填充,获取该parent的rate,
  determine_rate这个接口要注意,有这个接口在执行set_rate的过程中可能会更改父时钟

clk_set_parent

clk_set_parent
-->clk_core_set_parent_nolock
	-->p_index = clk_fetch_parent_index(core, parent);
		-->for(i = 0; i < core->num_parents; i++)
			{
				if (core->parents[i].core == parent)
					return i;
				if (core->parents[i].core)
					continue;
				if (core->parents[i].hw) {
					if (core->parents[i].hw == parent->hw)
						break;
					continue;
				}
				if (parent == clk_core_get(core, i))
					break;
				if (core->parents[i].name &&
					!strcmp(parent->name, core->parents[i].name))
					break;
			}
			return i;
		-->p_rate = parent->rate;
	-->__clk_set_parent
		-->old_parent = __clk_set_parent_before(core, parent);
		-->core->ops->set_parent(core->hw, p_index);
	-->__clk_recalc_rates(core, ABORT_RATE_CHANGE);
		-->core->rate = clk_recalc(core, parent_rate);
			-->core->ops->recalc_rate(core->hw, parent_rate);
		-->hlist_for_each_entry(child, &core->children, child_node)
			__clk_recalc_rates(child, msg);

  clk_fetch_parent_index中遍历所有parent,如果core->parents[i].core为空,则执行clk_core_get来获取parent的clk_core,否则通过name来,找得到返回index。然后执行set_parent,最后遍历子节点执行recalc_rate,奇怪了?这个set_parent之后会重新recalc_rate,但是不会执行set_rate,不合理啊

clk_ignore_unused

static bool clk_ignore_unused __initdata;
static int __init clk_ignore_unused_setup(char *__unused)
{
	clk_ignore_unused = true;
	return 1;
}
__setup("clk_ignore_unused", clk_ignore_unused_setup);

  bootargs中需要有clk_ignore_unused参数,会执行clk_ignore_unused_setup将clk_ignore_unused置为true,否则会执行clk_disable_unused

static int __init clk_disable_unused(void)
{
	struct clk_core *core;

	if (clk_ignore_unused) {
		pr_warn("clk: Not disabling unused clocks\n");
		return 0;
	}

	pr_warn("********************************************************************\n");
	pr_warn("**     NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE           **\n");
	pr_warn("**  If your kernel hangs after printing this message, it may be that clk_ignore_unused is not set in the bootargs attribute of uboot **\n");
	pr_warn("**     NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE           **\n");
	pr_warn("********************************************************************\n");
	clk_prepare_lock();

	hlist_for_each_entry(core, &clk_root_list, child_node)
		clk_disable_unused_subtree(core);

	hlist_for_each_entry(core, &clk_orphan_list, child_node)
		clk_disable_unused_subtree(core);

	hlist_for_each_entry(core, &clk_root_list, child_node)
		clk_unprepare_unused_subtree(core);

	hlist_for_each_entry(core, &clk_orphan_list, child_node)
		clk_unprepare_unused_subtree(core);

	clk_prepare_unlock();

	return 0;
}
&emsp;&emsp;late_initcall_sync(clk_disable_unused);

late_initcall会调用clk_disable_unused,没设置clk_ignore_unused为true的话会将

clk_set_rate_exclusive

  设置exclusive_count++ 独占访问

CLK_OF_DECLARE

  就是个宏,将函数放在指定section中,在很早的时候就调用了,函数内还是需要调用clk_register来册时钟

start_kernel
-->time_init
	-->of_clk_init
  • 17
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值