Linux 内核clk 硬件相关层

在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; }

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值