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