Linux clk子系统(4)--fixed-clk

it2025-11-08  13

目录

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,系统已经实现相应的接口。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

最新回复(0)