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