在Linux内核的clk处理框架中,平台需要实现针对具体clk操作的函数句柄,并且这些
被封装到struct clk_hw对象中。之后通过函数clk_register()向clk框架层注册。
框架层主要分配两个对象,一个struct clk对象,另外一个是struct clk_core对象
clk框架层主要通过这两个结构管理时钟。
我们看clk_register()实现:
/**
* clk_register - allocate a new clock, register it and return an opaque cookie
* @dev: device that is registering this clock
* @hw: link to hardware-specific clock data
*
* clk_register is the primary interface for populating the clock tree with new
* clock nodes.  It returns a pointer to the newly allocated struct clk which
* cannot be dereferenced by driver code but may be used in conjunction with the
* rest of the clock API.  In the event of an error clk_register will return an
* error code; drivers must test for an error code after calling 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;
}
} /* avoid unnecessary string look-ups of clk_core's possible parents. */
core->parents = kcalloc(core->num_parents, sizeof(*core->parents),
GFP_KERNEL);
if (!core->parents) {
ret = -ENOMEM;
goto fail_parents;
}; INIT_HLIST_HEAD(&core->clks); hw->clk = __clk_create_clk(hw, NULL, NULL);CONFIG_COMMON_CLK需要配置
if (IS_ERR(hw->clk)) {
ret = PTR_ERR(hw->clk);
goto fail_parents;
} ret = __clk_core_init(core);
if (!ret)
return hw->clk; __clk_free_clk(hw->clk);
hw->clk = NULL;fail_parents:
kfree(core->parents);
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);
}
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 (IS_ERR_OR_NULL(hw))
return ERR_CAST(hw); clk = kzalloc(sizeof(*clk), GFP_KERNEL);
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_prepare_unlock(); return clk;
}

/**
* __clk_core_init - initialize the data structures in a struct clk_core
* @core: clk_core being initialized
*
* Initializes the lists in struct clk_core, queries the hardware for the
* parent and rate and sets them both.
*/
static int __clk_core_init(struct clk_core *core)
{
int i, ret = 0;
struct clk_core *orphan;
struct hlist_node *tmp2;
unsigned long rate; if (!core)
return -EINVAL; clk_prepare_lock(); /* check to see if a clock with this name is already registered */
if (clk_core_lookup(core->name)) {
pr_debug("%s: clk %s already initialized\n",
__func__, core->name);
ret = -EEXIST;
goto out;
} /* check that clk_ops are sane.  See Documentation/clk.txt */
if (core->ops->set_rate &&
!((core->ops->round_rate || core->ops->determine_rate) &&
core->ops->recalc_rate)) {
pr_err("%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_err("%s: %s must implement .get_parent & .set_parent\n",
__func__, core->name);
ret = -EINVAL;
goto out;
} if (core->num_parents > 1 && !core->ops->get_parent) {
pr_err("%s: %s must implement .get_parent as it has multi parents\n",
__func__, core->name);
ret = -EINVAL;
goto out;
} if (core->ops->set_rate_and_parent &&
!(core->ops->set_parent && core->ops->set_rate)) {
pr_err("%s: %s must implement .set_parent & .set_rate\n",
__func__, core->name);
ret = -EINVAL;
goto out;
} /* throw a WARN if any entries in parent_names are NULL */
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); core->parent = __clk_init_parent(core); /*
* Populate core->parent if parent has already been clk_core_init'd. If
* parent has not yet been clk_core_init'd then place clk in the orphan
* list.  If clk doesn't have any parents then place it in the root
* clk list.
*
* Every time a new clk is clk_init'd then we walk the list of orphan
* clocks and re-parent any that are children of the clock currently
* being clk_init'd.
*/
if (core->parent) {
hlist_add_head(&core->child_node,
&core->parent->children);
core->orphan = core->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;
} /*
* Set clk's accuracy.  The preferred method is to use
* .recalc_accuracy. For simple clocks and lazy developers the default
* fallback is to use the parent's accuracy.  If a clock doesn't have a
* parent (or is orphaned) then accuracy is set to zero (perfect
* clock).
*/
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; /*
* Set clk's phase.
* Since a phase is by definition relative to its parent, just
* query the current clock phase, or just assume it's in phase.
*/
if (core->ops->get_phase)
core->phase = core->ops->get_phase(core->hw);
else
core->phase = 0; /*
* Set clk's rate.  The preferred method is to use .recalc_rate.  For
* simple clocks and lazy developers the default fallback is to use the
* parent's rate.  If a clock doesn't have a parent (or is orphaned)
* then rate is set to zero.
*/
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; /*
* walk the list of orphan clocks and reparent any that newly finds a
* parent.
*/
hlist_for_each_entry_safe(orphan, tmp2, &clk_orphan_list, child_node) {
struct clk_core *parent = __clk_init_parent(orphan);  /*
* we could call __clk_set_parent, but that would result in a
* redundant call to the .set_rate op, if it exists
*/
if (parent) {
__clk_set_parent_before(orphan, parent);
__clk_set_parent_after(orphan, parent, NULL);
__clk_recalc_accuracies(orphan);
__clk_recalc_rates(orphan, 0);
}
} /*
* optional platform-specific magic
*
* The .init callback is not used by any of the basic clock types, but
* exists for weird hardware that must perform initialization magic.
* Please consider other ways of solving initialization problems before
* using this callback, as its use is discouraged.
*/
if (core->ops->init)
core->ops->init(core->hw); if (core->flags & CLK_IS_CRITICAL) {
unsigned long flags;  clk_core_prepare(core);  flags = clk_enable_lock();
clk_core_enable(core);
clk_enable_unlock(flags);
} kref_init(&core->ref);
out:
clk_prepare_unlock(); if (!ret)
clk_debug_register(core); return ret;
}
 

Linux 内核clk 硬件相关层相关推荐

  1. Linux系统查看硬件相关信息

    Linux系统硬件信息查看 一.前言 二.查看CPU相关信息 1.利用文件进行查看:cat /proc/cpuinfo 2.利用命令查看:lscpu (调取cpuinfo文件中的内容) 三.负载情况( ...

  2. Linux内核数据包L3层转发处理流程

    转载: https://blog.csdn.net/eric_liufeng/article/details/10789811 https://blog.csdn.net/shichaog/artic ...

  3. linux 内核网络协议栈--IP层开始直到包被处理(三)

    先看看ip头结构: struct iphdr struct iphdr {#if defined(__LITTLE_ENDIAN_BITFIELD) // 小端__u8 ihl:4, // 首部长度( ...

  4. Linux内核的framebuffer相关的内核代码注释

    由于现在正在进行framebuffer的一个项目开发,所以需要学习framebuffer的内核代码,今天现在这里上传我的相关注释,后面再将我的学习内容和相关framebuffer的操作提交上来. 位置 ...

  5. linux内核配置cpu相关,Linux内核配置

    对内核的操作分为两类: 一.配置内核 二.编译内核 一.配置内核的过程如下: make menuconfig时可以修改配置项,这主要是Kconfig的功能, 然后最终的配置结果会保存在.config文 ...

  6. linux内核网络协议栈--2层报文处理(十七)

    版本说明 Linux版本: 3.10.103 网卡驱动: ixgbe 网络协议注册 br_add_if主要是注册桥处理函数br_handle_frame给skb->edv->rx_hand ...

  7. Linux 内核clk 添加clk provider

    内核把所有的clk provider管理维护起来,这些存放在of_clk_providers链表当中, 这个provider定义如下: struct of_clk_provider {struct l ...

  8. linux内核网络协议栈--ip层报文转发之ip_local_out()函数(六)

    IP层本地报文发送有两个函数ip_local_out和ip_local_out_sk,实际实现两者是等同的,因为本地发送的报文,skb必然关联着一个sock对象. 1.ip_local_out函数 s ...

  9. linux内核同步机制相关收集

    memory barrier http://www.wowotech.net/kernel_synchronization/memory-barrier.html 转载于:https://www.cn ...

最新文章

  1. 阵元间隔为半波长的均匀分布16元线阵
  2. Spring 中的隔离级别
  3. 计算机考研问题,考研计算机常见的6个问题
  4. 机器学习 -- 用户画像
  5. form表单target的用法
  6. 如何实现消息功能_微信多个群转发消息如何实现?多群转发小助手转播云端转播功能怎样操作?...
  7. pytorch:测试GPU是否可用
  8. linux自动微信发信息,Linux下发送微信消息
  9. 实验项目三 基于A*搜索算法迷宫游戏开发
  10. java 同比数据怎么算的_如何计算同比的计算公式?
  11. 搜索方法_如何搜索到自己的CSDN博客
  12. Javascript验证身份证号码:正则表达式
  13. 我学了这套性能优化方法论,领导年终奖给我发了6个月
  14. win10专业版和企业版的区别
  15. 中科大网上财务报销填写流程
  16. window.open打开子窗口回调父窗口函数
  17. Google 最大数据中心发生爆炸,三名技术人员受伤
  18. Codeforces Global Round 2 B. Alyona and a Narrow Fridge
  19. PID控制及整定算法
  20. 量子涡轮服务器原理,纯mek用一个泵跑满涡轮——水冷聚变堆的搭建

热门文章

  1. BT种子文件格式和Bencoding编码
  2. 并非所有投资都同样贬值:数字资产的恢复期
  3. Linux启动定时任务
  4. QuickRedis 是一款 Redis 可视化管理工具
  5. 斯德哥尔摩的照片七:城市漫步(下)
  6. 教育部明确:坚持普职分流非常必要
  7. 电力电子技术第二章总结
  8. CIC 滤波器——不同长度的单级CIC滤波器的频谱特性
  9. 【06】Spring源码-分析篇-ApplicationContext
  10. 超大容量充电宝哪个好,大容量移动充电宝哪个品牌好?