目录
1. fixed-clk的注册流程
2. clk_register_fixed_rate_with_accuracy
3. clk_hw_register_fixed_rate_with_accuracy
4. clk_hw_register
5. clk_register
6. __clk_create_clk
7. __clk_core_init
8. of_clk_add_provider
9. of_clk_src_simple_get
10. fixed-clk总结
1. fixed-clk的注册流程
driver/clk/clk-leo.c中leo_clk_match的.data指向的函数,
以leo_clk_match数组的第一个元素的为fixed-clock(固定频率,描述的有源晶振、无源晶振、内部高频振荡器、内部低频振荡器,对应设备树中compatible为“fixed-clock”的节点,leo没有使用这种方式)。
/**
* driver/clk/faraday/clk-leo.c
*/
/**
* leo_clk_match数组的第一个元素,第一个元素为fixed-clock,
* 调用系统提供of_fixed_clk_setup进行配置
*/
{ .compatible = "leo,osc0", .data = of_fixed_clk_setup, },
/**
* dts中leo_clk_match数组的第一个元素对应的设备节点
*/
osc0: osc0-25mhz {
#clock-cells = <0>;
compatible = "leo,osc0";
clock-frequency = <25000000>;
clock-output-names = "osc0";
};
/**
* drivers/clk/clk-fixed-rate.c
*/
/**
* of_fixed_clk_setup() - Setup function for simple fixed rate clock
*/
void __init of_fixed_clk_setup(struct device_node *node)
{
_of_fixed_clk_setup(node);
}
/**
* drivers/clk/clk-fixed-rate.c
*/
static struct clk *_of_fixed_clk_setup(struct device_node *node)
{
struct clk *clk;
const char *clk_name = node->name;
u32 rate;
u32 accuracy = 0;
int ret;
if (of_property_read_u32(node, "clock-frequency", &rate))
return ERR_PTR(-EIO);
of_property_read_u32(node, "clock-accuracy", &accuracy);
of_property_read_string(node, "clock-output-names", &clk_name);
/**
* 见2. clk_register_fixed_rate_with_accuracy
*/
clk = clk_register_fixed_rate_with_accuracy(NULL, clk_name, NULL,
0, rate, accuracy);
if (IS_ERR(clk))
return clk;
/**
* 见8. of_clk_add_provider
* 见9. of_clk_src_simple_get
*/
ret = of_clk_add_provider(node, of_clk_src_simple_get, clk);
if (ret) {
clk_unregister(clk);
return ERR_PTR(ret);
}
return clk;
}
2. clk_register_fixed_rate_with_accuracy
/**
* drivers/clk/clk-fixed-rate.c
*/
struct clk *clk_register_fixed_rate_with_accuracy(struct device *dev,
const char *name, const char *parent_name, unsigned long flags,
unsigned long fixed_rate, unsigned long fixed_accuracy)
{
struct clk_hw *hw;
/**
* 见3. clk_hw_register_fixed_rate_with_accuracy
*/
hw = clk_hw_register_fixed_rate_with_accuracy(dev, name, parent_name,
flags, fixed_rate, fixed_accuracy);
if (IS_ERR(hw))
return ERR_CAST(hw);
return hw->clk;
}
3. clk_hw_register_fixed_rate_with_accuracy
"include/linux/clk-provider.h"
/**
* struct clk_fixed_rate - fixed-rate clock
* @hw: handle between common and hardware-specific interfaces
* @fixed_rate: constant frequency of clock
*/
struct clk_fixed_rate {
struct clk_hw hw;
unsigned long fixed_rate;
unsigned long fixed_accuracy;
u8 flags;
};
/**
* drivers/clk/clk-fixed-rate.c
*/
/**
* clk_hw_register_fixed_rate_with_accuracy - register fixed-rate clock with
* the clock framework
* @dev: device that is registering this clock
* @name: name of this clock
* @parent_name: name of clock's parent
* @flags: framework-specific flags
* @fixed_rate: non-adjustable clock rate
* @fixed_accuracy: non-adjustable clock rate
*/
struct clk_hw *clk_hw_register_fixed_rate_with_accuracy(struct device *dev,
const char *name, const char *parent_name, unsigned long flags,
unsigned long fixed_rate, unsigned long fixed_accuracy)
{
struct clk_fixed_rate *fixed;
struct clk_hw *hw;
struct clk_init_data init;
int ret;
/* allocate fixed-rate clock */
fixed = kzalloc(sizeof(*fixed), GFP_KERNEL);
if (!fixed)
return ERR_PTR(-ENOMEM);
init.name = name;
init.ops = &clk_fixed_rate_ops;
init.flags = flags | CLK_IS_BASIC;
init.parent_names = (parent_name ? &parent_name: NULL);
init.num_parents = (parent_name ? 1 : 0);
/* struct clk_fixed_rate assignments */
fixed->fixed_rate = fixed_rate;
fixed->fixed_accuracy = fixed_accuracy;
fixed->hw.init = &init;
/* register the clock */
hw = &fixed->hw;
/**
* 见4. clk_hw_register
*/
ret = clk_hw_register(dev, hw);
if (ret) {
kfree(fixed);
hw = ERR_PTR(ret);
}
return hw;
}
/**
* drivers/clk/clk-fixed-rate.c
*/
const struct clk_ops clk_fixed_rate_ops = {
.recalc_rate = clk_fixed_rate_recalc_rate,
.recalc_accuracy = clk_fixed_rate_recalc_accuracy,
};
/**
* drivers/clk/clk-fixed-rate.c
*/
static unsigned long clk_fixed_rate_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
return to_clk_fixed_rate(hw)->fixed_rate;
}
static unsigned long clk_fixed_rate_recalc_accuracy(struct clk_hw *hw,
unsigned long parent_accuracy)
{
return to_clk_fixed_rate(hw)->fixed_accuracy;
}
4. clk_hw_register
/**
* drivers/clk/clk.c
*/
/**
* clk_hw_register - register a clk_hw and return an error code
* @dev: device that is registering this clock
* @hw: link to hardware-specific clock data
*
* clk_hw_register is the primary interface for populating the clock tree with
* new clock nodes. It returns an integer equal to zero indicating success or
* less than zero indicating failure. Drivers must test for an error code after
* calling clk_hw_register().
*/
int clk_hw_register(struct device *dev, struct clk_hw *hw)
{
/**
* 见5. clk_register
*/
return PTR_ERR_OR_ZERO(clk_register(dev, hw));
}
5. clk_register
/**
* drivers/clk/clk.c
*/
/**
* 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);
/**
* 添加调试打印信息如下:
* printk("core->name:%s,
* core->num_parents:%d,
* core->parent_names:%p\n",
* core->name,
* core->num_parents,
* core->parent_names);
* core->name:osc0, core->num_parents:0, core->parent_names:00000010
* core->name:rosc0, core->num_parents:0, core->parent_names:00000010
*/
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);
/**
* 见6. __clk_create_clk
*/
hw->clk = __clk_create_clk(hw, NULL, NULL);
if (IS_ERR(hw->clk)) {
ret = PTR_ERR(hw->clk);
goto fail_parents;
}
/**
* 见7. __clk_create_clk
*/
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);
}
6. __clk_create_clk
/**
* drivers/clk/clk.c
*/
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 = kstrdup_const(con_id, GFP_KERNEL);
clk->max_rate = ULONG_MAX;
clk_prepare_lock();
hlist_add_head(&clk->clks_node, &hw->core->clks);
clk_prepare_unlock();
return clk;
}
7. __clk_core_init
/**
* drivers/clk/clk.c
*/
/**
* __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;
/*
* Enable CLK_IS_CRITICAL clocks so newly added critical clocks
* don't get accidentally disabled when walking the orphan tree and
* reparenting clocks
*/
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);
}
/*
* 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 need to use __clk_set_parent_before() and _after() to
* to properly migrate any prepare/enable count of the orphan
* clock. This is important for CLK_IS_CRITICAL clocks, which
* are enabled during init but might not have a parent yet.
*/
if (parent) {
/* update the clk tree topology */
__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);
kref_init(&core->ref);
out:
clk_prepare_unlock();
if (!ret)
clk_debug_register(core);
return ret;
}
/**
* drivers/clk/clk.c
*/
static unsigned long clk_core_get_rate_nolock(struct clk_core *core)
{
unsigned long ret;
if (!core) {
ret = 0;
goto out;
}
ret = core->rate;
if (!core->num_parents)
goto out;
if (!core->parent)
ret = 0;
out:
return ret;
8. of_clk_add_provider
/**
* drivers/clk/clk.c
*/
/**
* of_clk_add_provider() - Register a clock provider for a node
* @np: Device node pointer associated with clock provider
* @clk_src_get: callback for decoding clock
* @data: context pointer for @clk_src_get callback.
*/
int of_clk_add_provider(struct device_node *np,
struct clk *(*clk_src_get)(struct of_phandle_args *clkspec,
void *data),
void *data)
{
struct of_clk_provider *cp;
int ret;
cp = kzalloc(sizeof(*cp), GFP_KERNEL);
if (!cp)
return -ENOMEM;
cp->node = of_node_get(np);
cp->data = data;
cp->get = clk_src_get;
mutex_lock(&of_clk_mutex);
list_add(&cp->link, &of_clk_providers);
mutex_unlock(&of_clk_mutex);
pr_debug("Added clock from %pOF\n", np);
/**
* 加打印调试ret = 0
*/
ret = of_clk_set_defaults(np, true);
if (ret < 0)
of_clk_del_provider(np);
return ret;
}
EXPORT_SYMBOL_GPL(of_clk_add_provider);
/**
* drivers/clk/clk-conf.c
*/
/**
* of_clk_set_defaults() - parse and set assigned clocks configuration
* @node: device node to apply clock settings for
* @clk_supplier: true if clocks supplied by @node should also be considered
*
* This function parses 'assigned-{clocks/clock-parents/clock-rates}' properties
* and sets any specified clock parents and rates. The @clk_supplier argument
* should be set to true if @node may be also a clock supplier of any clock
* listed in its 'assigned-clocks' or 'assigned-clock-parents' properties.
* If @clk_supplier is false the function exits returning 0 as soon as it
* determines the @node is also a supplier of any of the clocks.
*/
int of_clk_set_defaults(struct device_node *node, bool clk_supplier)
{
int rc;
if (!node)
return 0;
rc = __set_clk_parents(node, clk_supplier);
if (rc < 0)
return rc;
return __set_clk_rates(node, clk_supplier);
}
EXPORT_SYMBOL_GPL(of_clk_set_defaults);
/**
* drivers/clk/clk-conf.c
*/
static int __set_clk_parents(struct device_node *node, bool clk_supplier)
{
struct of_phandle_args clkspec;
int index, rc, num_parents;
struct clk *clk, *pclk;
num_parents = of_count_phandle_with_args(node, "assigned-clock-parents",
"#clock-cells");
/**
* 对应的device_node中未定义assigned-clock-parents属性,
* num_parents返回 -2,EINVAL是22,下面的pr_err也不会执行
*/
if (num_parents == -EINVAL)
pr_err("clk: invalid value of clock-parents property at %pOF\n",
node);
for (index = 0; index < num_parents; index++) {
rc = of_parse_phandle_with_args(node, "assigned-clock-parents",
"#clock-cells", index, &clkspec);
if (rc < 0) {
/* skip empty (null) phandles */
if (rc == -ENOENT)
continue;
else
return rc;
}
if (clkspec.np == node && !clk_supplier)
return 0;
pclk = of_clk_get_from_provider(&clkspec);
if (IS_ERR(pclk)) {
if (PTR_ERR(pclk) != -EPROBE_DEFER)
pr_warn("clk: couldn't get parent clock %d for %pOF\n",
index, node);
return PTR_ERR(pclk);
}
rc = of_parse_phandle_with_args(node, "assigned-clocks",
"#clock-cells", index, &clkspec);
if (rc < 0)
goto err;
if (clkspec.np == node && !clk_supplier) {
rc = 0;
goto err;
}
clk = of_clk_get_from_provider(&clkspec);
if (IS_ERR(clk)) {
if (PTR_ERR(clk) != -EPROBE_DEFER)
pr_warn("clk: couldn't get assigned clock %d for %pOF\n",
index, node);
rc = PTR_ERR(clk);
goto err;
}
rc = clk_set_parent(clk, pclk);
if (rc < 0)
pr_err("clk: failed to reparent %s to %s: %d\n",
__clk_get_name(clk), __clk_get_name(pclk), rc);
clk_put(clk);
clk_put(pclk);
}
return 0;
err:
clk_put(pclk);
return rc;
}
/**
* drivers/clk/clk-conf.c
*/
static int __set_clk_rates(struct device_node *node, bool clk_supplier)
{
struct of_phandle_args clkspec;
struct property *prop;
const __be32 *cur;
int rc, index = 0;
struct clk *clk;
u32 rate;
of_property_for_each_u32(node, "assigned-clock-rates", prop, cur, rate) {
if (rate) {
rc = of_parse_phandle_with_args(node, "assigned-clocks",
"#clock-cells", index, &clkspec);
if (rc < 0) {
/* skip empty (null) phandles */
if (rc == -ENOENT)
continue;
else
return rc;
}
if (clkspec.np == node && !clk_supplier)
return 0;
clk = of_clk_get_from_provider(&clkspec);
if (IS_ERR(clk)) {
if (PTR_ERR(clk) != -EPROBE_DEFER)
pr_warn("clk: couldn't get clock %d for %pOF\n",
index, node);
return PTR_ERR(clk);
}
rc = clk_set_rate(clk, rate);
if (rc < 0)
pr_err("clk: couldn't set %s clk rate to %u (%d), current rate: %lu\n",
__clk_get_name(clk), rate, rc,
clk_get_rate(clk));
clk_put(clk);
}
index++;
}
return 0;
}
9. of_clk_src_simple_get
/**
* drivers/clk/clk.c
*/
struct clk_hw *of_clk_hw_simple_get(struct of_phandle_args *clkspec, void *data)
{
return data;
}
EXPORT_SYMBOL_GPL(of_clk_hw_simple_get);
10. fixed-clk总结
fixed-clk类型的clk为固定频率的,描述系统使用的外部晶振(有源晶振和无源晶体),内部高速低速晶振类型的clk,系统已经实现相应的接口。