Linux clock driver(2) clk_register 详解
来源:互联网 发布:叶如何知秋 编辑:程序博客网 时间:2024/06/08 06:48
clk_register 详解
clk_register是底层clock driver用来向CCF(common clock framework)层注册时钟节点的接口,是CCF中的关键函数之一,下面将结合程序详细介绍其功能。
先看一下来看一下 clk_register 函数的实现:
struct clk *clk_register(struct device *dev, struct clk_hw *hw) { int i, ret; struct clk_core *core; core = kzalloc(sizeof(*core), GFP_KERNEL); if (!core) { ret = -ENOMEM; goto fail_out; } core->name = kstrdup_const(hw->init->name, GFP_KERNEL); if (!core->name) { ret = -ENOMEM; goto fail_name; } core->ops = hw->init->ops; if (dev && dev->driver) core->owner = dev->driver->owner; core->hw = hw; core->flags = hw->init->flags; core->num_parents = hw->init->num_parents; core->min_rate = 0; core->max_rate = ULONG_MAX; hw->core = core; /* allocate local copy in case parent_names is __initdata */ core->parent_names = kcalloc(core->num_parents, sizeof(char *), GFP_KERNEL); if (!core->parent_names) { ret = -ENOMEM; goto fail_parent_names; } /* copy each string name in case parent_names is __initdata */ for (i = 0; i < core->num_parents; i++) { core->parent_names[i] = kstrdup_const(hw->init->parent_names[i], GFP_KERNEL); if (!core->parent_names[i]) { ret = -ENOMEM; goto fail_parent_names_copy; } } /*创建哈希表头,每次调用get_clk接口都会创建一个clk实例,并将其加入此哈希表中, 注:每个clock由一个struct clk_core描述,其与struct clk_hw是一一对应的关系, 但是struct clk可能有很多个,其他驱动需要操作clock时,都需要先分配一个 struct clk 类型的指针,因此其与struct clk_core是一对多的关系, 也可以说clk是clk_core的实例*/ INIT_HLIST_HEAD(&core->clks); hw->clk = __clk_create_clk(hw, NULL, NULL); if (IS_ERR(hw->clk)) { ret = PTR_ERR(hw->clk); goto fail_parent_names_copy; } ret = __clk_init(dev, hw->clk); if (!ret) return hw->clk; __clk_free_clk(hw->clk); hw->clk = NULL; fail_parent_names_copy: while (--i >= 0) kfree_const(core->parent_names[i]); kfree(core->parent_names); fail_parent_names: kfree_const(core->name); fail_name: kfree(core); fail_out: return ERR_PTR(ret); } EXPORT_SYMBOL_GPL(clk_register);
此函数的主要功能可分为3点:
1. 分配struct clk_core 类型的变量,并依据传入的hw指针对其进行初始化
2. 调用 __clk_create_clk 函数分配struct clk类型的变量
3. 调用__clk_init完成clock 的初始化
__clk_create_clk 函数的实现如下:
struct clk *__clk_create_clk(struct clk_hw *hw, const char *dev_id, const char *con_id) { struct clk *clk; /* This is to allow this function to be chained to others */ if (!hw || IS_ERR(hw)) return (struct clk *) hw; clk = kzalloc(sizeof(*clk), GFP_KERNEL); //分配struct clk类型的变量 if (!clk) return ERR_PTR(-ENOMEM); clk->core = hw->core; //填充部分元素 clk->dev_id = dev_id; clk->con_id = con_id; clk->max_rate = ULONG_MAX; clk_prepare_lock(); hlist_add_head(&clk->clks_node, &hw->core->clks); 将此clk节点加入core->clks队列中 clk_prepare_unlock(); return clk; }
__clk_init 函数实现如下:
static int __clk_init(struct device *dev, struct clk *clk_user) { int i, ret = 0; struct clk_core *orphan; struct hlist_node *tmp2; struct clk_core *core; unsigned long rate; if (!clk_user) return -EINVAL; core = clk_user->core; clk_prepare_lock(); /* 判断此clk是否已经注册过了 */ if (clk_core_lookup(core->name)) { pr_debug("%s: clk %s already initialized\n", __func__, core->name); ret = -EEXIST; goto out; } /* 判断底层clock的操作函数的设计是否符合规定. 可参考 Documentation/clk.txt */ if (core->ops->set_rate && !((core->ops->round_rate || core->ops->determine_rate) && core->ops->recalc_rate)) { pr_warning("%s: %s must implement .round_rate or .determine_rate in addition to .recalc_rate\n", __func__, core->name); ret = -EINVAL; goto out; } if (core->ops->set_parent && !core->ops->get_parent) { pr_warning("%s: %s must implement .get_parent & .set_parent\n", __func__, core->name); ret = -EINVAL; goto out; } if (core->ops->set_rate_and_parent && !(core->ops->set_parent && core->ops->set_rate)) { pr_warn("%s: %s must implement .set_parent & .set_rate\n", __func__, core->name); ret = -EINVAL; goto out; } /* 如果有parent name为null,则产生WARN */ for (i = 0; i < core->num_parents; i++) WARN(!core->parent_names[i], "%s: invalid NULL in %s's .parent_names\n", __func__, core->name); /* * Allocate an array of struct clk *'s to avoid unnecessary string * look-ups of clk's possible parents. This can fail for clocks passed * in to clk_init during early boot; thus any access to core->parents[] * must always check for a NULL pointer and try to populate it if * necessary. * * If core->parents is not NULL we skip this entire block. This allows * for clock drivers to statically initialize core->parents. */ if (core->num_parents > 1 && !core->parents) { core->parents = kcalloc(core->num_parents, sizeof(struct clk *), GFP_KERNEL); /* * clk_core_lookup returns NULL for parents that have not been * clk_init'd; thus any access to clk->parents[] must check * for a NULL pointer. We can always perform lazy lookups for * missing parents later on. */ if (core->parents) for (i = 0; i < core->num_parents; i++) core->parents[i] = clk_core_lookup(core->parent_names[i]); } core->parent = __clk_init_parent(core); /*判断此节点是否为孤儿节点,如果有parent且parent已经初始化,则其延续父节点的特性,如果为ROOT节点,就肯定不是孤儿节点了,否则为孤儿节点*/if (core->parent) { hlist_add_head(&core->child_node, &core->parent->children); core->orphan = core->parent->orphan; } else if (core->flags & CLK_IS_ROOT) { 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; } /*此处应该是设置精度,但是从我们的底层驱动看,这个都是设置为默认值0*/if (core->ops->recalc_accuracy) core->accuracy = core->ops->recalc_accuracy(core->hw, __clk_get_accuracy(core->parent)); else if (core->parent) core->accuracy = core->parent->accuracy; else core->accuracy = 0; /*设置相位,默认为0*/ if (core->ops->get_phase) core->phase = core->ops->get_phase(core->hw); else core->phase = 0; /*设置时钟频率,如果实现了recalc_rate函数,则通过此函数获取,如果有parent,则和parent频率一致,否则设置为0*/ if (core->ops->recalc_rate) rate = core->ops->recalc_rate(core->hw, clk_core_get_rate_nolock(core->parent));else if (core->parent) rate = core->parent->rate; else rate = 0; core->rate = core->req_rate = rate; /*查看当前结构是否为某些孤儿节点的parent,如果是,则reparent*/ hlist_for_each_entry_safe(orphan, tmp2, &clk_orphan_list, child_node) { if (orphan->num_parents && orphan->ops->get_parent) { i = orphan->ops->get_parent(orphan->hw); if (i >= 0 && i < orphan->num_parents && !strcmp(core->name, orphan->parent_names[i])) clk_core_reparent(orphan, core); continue; } /*如果没有实现get_parent,则遍历parents列表*/ for (i = 0; i < orphan->num_parents; i++) if (!strcmp(core->name, orphan->parent_names[i])) { clk_core_reparent(orphan, core); break; } } /*如无特殊硬件层面的限制,不建议实现此init函数*/ if (core->ops->init) core->ops->init(core->hw); kref_init(&core->ref); out: clk_prepare_unlock(); if (!ret) clk_debug_register(core); return ret; }
阅读全文
1 0
- Linux clock driver(2) clk_register 详解
- Linux clock driver
- clk_register
- Real Time Clock Driver for Linux
- linux clock
- linux clock
- Linux(6)RedHat7 基本命令五-hwclock(clock)命令详解
- linux driver 面试2
- Linux Driver 入门-2
- linux i2c driver 2
- Linux common clock framework(2)_clock provider
- Linux common clock framework(2)_概述
- Linux common clock framework(2)_clock provider
- Linux common clock framework(2)_clock provider
- zoj3131 Digital Clock(2)
- arm linux clock implement
- Linux Clock & Time
- Linux Clock & Time
- Android wifi调试
- 批量导入导出windows计划任务的批处理脚本
- 拿什么拯救你,我的双眼
- 做用户测试的10个正确姿势
- BottomTabBar替换RadioButton切换Fragment
- Linux clock driver(2) clk_register 详解
- v$sql与v_$sql相互关系
- Makefile 中使用的函数
- int转换为string的两种方法(to_string、字符串流)
- 1101---递归求解
- java判断某个点是否在所画范围内(多边形/圆形)
- 【十八掌●内功篇】第五掌:HDFS之基础知识
- 解决tomcat闪退问题
- 博弈论模板