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