头文件: include/linux/clk-provider.h 实现文件: 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 conjuction 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); struct clk *devm_clk_register(struct device *dev, struct clk_hw *hw);
/* * DOC: Basic clock implementations common to many platforms * * Each basic clock hardware type is comprised of a structure describing the * clock hardware, implementations of the relevant callbacks in struct clk_ops, * unique flags for that hardware type, a registration function and an * alternative macro for static initialization */
/** * struct clk_fixed_rate - fixed-rate clock * @hw: handle between common and hardware-specific interfaces * @fixed_rate: constant frequency of clock */ structclk_fixed_rate { structclk_hwhw; unsignedlong fixed_rate; unsignedlong fixed_accuracy; u8 flags; };
实现文件: drivers/clk/clk-gate.c /** * struct clk_gate - gating clock * * @hw: handle between common and hardware-specific interfaces * @reg: register controlling gate * @bit_idx: single bit controlling gate * @flags: hardware-specific flags * @lock: register lock * * Clock which can gate its output. Implements .enable & .disable * * Flags: * CLK_GATE_SET_TO_DISABLE - by default this clock sets the bit at bit_idx to * enable the clock. Setting this flag does the opposite: setting the bit * disable the clock and clearing it enables the clock * CLK_GATE_HIWORD_MASK - The gate settings are only in lower 16-bit * of this register, and mask of gate bits are in higher 16-bit of this * register. While setting the gate bits, higher 16-bit should also be * updated to indicate changing gate bits. */ structclk_gate { structclk_hwhw; void __iomem *reg; u8 bit_idx; u8 flags; spinlock_t *lock; };
/** * struct clk_fixed_factor - fixed multiplier and divider clock * * @hw: handle between common and hardware-specific interfaces * @mult: multiplier * @div: divider * * Clock with a fixed multiplier and divider. The output frequency is the * parent clock rate divided by div and multiplied by mult. * Implements .recalc_rate, .set_rate and .round_rate */
实现文件: drivers/clk/clk-fractional-divider.c /** * struct clk_fractional_divider - adjustable fractional divider clock * * @hw: handle between common and hardware-specific interfaces * @reg: register containing the divider * @mshift: shift to the numerator bit field * @mwidth: width of the numerator bit field * @nshift: shift to the denominator bit field * @nwidth: width of the denominator bit field * @max_parent: the maximum frequency of fractional divider parent clock * @lock: register lock * * Clock with adjustable fractional divider affecting its output frequency. */ structclk_fractional_divider { structclk_hwhw; void __iomem *reg; u8 mshift; u8 mwidth; u32 mmask; u8 nshift; u8 nwidth; u32 nmask; u8 flags; unsignedlong max_prate; void (*approximation)(struct clk_hw *hw, unsignedlong rate, unsignedlong *parent_rate, unsignedlong *m, unsignedlong *n); spinlock_t *lock; };
实现文件: drivers/clk/clk-composite.c /*** * struct clk_composite - aggregate clock of mux, divider and gate clocks * * @hw: handle between common and hardware-specific interfaces * @mux_hw: handle between composite and hardware-specific mux clock * @rate_hw: handle between composite and hardware-specific rate clock * @gate_hw: handle between composite and hardware-specific gate clock * @brother_hw: a member of clk_composite who has the common parent clocks * with another clk_composite, and it's also a handle between * common and hardware-specific interfaces * @mux_ops: clock ops for mux * @rate_ops: clock ops for rate * @gate_ops: clock ops for gate */ structclk_composite { structclk_hwhw; structclk_opsops;
实现文件: drivers/clk/clk-gpio-gate.c /*** * struct clk_gpio_gate - gpio gated clock * * @hw: handle between common and hardware-specific interfaces * @gpiod: gpio descriptor * * Clock with a gpio control for enabling and disabling the parent clock. * Implements .enable, .disable and .is_enabled */
/** * struct clk_gpio_mux - gpio controlled clock multiplexer * * @hw: see struct clk_gpio * @gpiod: gpio descriptor to select the parent of this clock multiplexer * * Clock with a gpio control for selecting the parent clock. * Implements .get_parent, .set_parent and .determine_rate */
实现文件: 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; structclk_core *core;
/* allocate local copy in case parent_names is __initdata */ core->parent_names = kcalloc(core->num_parents, sizeof(char *), GFP_KERNEL); ......
/* 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); ...... }
/* avoid unnecessary string look-ups of clk_core's possible parents. */ core->parents = kcalloc(core->num_parents, sizeof(*core->parents), GFP_KERNEL); ......
/** * __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. */ staticint __clk_core_init(struct clk_core *core) { int i, ret; structclk_core *orphan; structhlist_node *tmp2; unsignedlong rate;
if (!core) return -EINVAL;
clk_prepare_lock();
ret = clk_pm_runtime_get(core); if (ret) goto unlock;
/* 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/driver-api/clk.rst */ 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; } elseif (!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; }
/* * 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);
/* * 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)); elseif (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 duty cycle. */ clk_core_update_duty_cycle_nolock(core);
/* * 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)); elseif (core->parent) rate = core->parent->rate; else rate = 0; core->rate = core->req_rate = rate;
core->boot_enabled = clk_core_is_enabled(core);
/* * 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) { unsignedlong flags;
ret = clk_core_prepare(core); if (ret) goto out;
flags = clk_enable_lock(); ret = clk_core_enable(core); clk_enable_unlock(flags); if (ret) { clk_core_unprepare(core); goto out; } }
clk_core_hold_state(core);
/* * 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) { structclk_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); __clk_core_update_orphan_hold_state(orphan); } }
最主要的两个API: /** * clk_get - lookup and obtain a reference to a clock producer. * @dev: device for clock "consumer" * @id: clock consumer ID * * Returns a struct clk corresponding to the clock producer, or * valid IS_ERR() condition containing errno. The implementation * uses @dev and @id to determine the clock consumer, and thereby * the clock producer. (IOW, @id may be identical strings, but * clk_get may return different clock producers depending on @dev.) * * Drivers must assume that the clock source is not enabled. * * clk_get should not be called from within interrupt context. */ struct clk *clk_get(struct device *dev, constchar *id); /** * devm_clk_get - lookup and obtain a managed reference to a clock producer. * @dev: device for clock "consumer" * @id: clock consumer ID * * Returns a struct clk corresponding to the clock producer, or * valid IS_ERR() condition containing errno. The implementation * uses @dev and @id to determine the clock consumer, and thereby * the clock producer. (IOW, @id may be identical strings, but * clk_get may return different clock producers depending on @dev.) * * Drivers must assume that the clock source is not enabled. * * devm_clk_get should not be called from within interrupt context. * * The clock will automatically be freed when the device is unbound * from the bus. */ struct clk *devm_clk_get(struct device *dev, constchar *id);
struct clk *clk_get(struct device *dev, constchar *con_id) { constchar *dev_id = dev ? dev_name(dev) : NULL; structclk *clk; if (dev) { clk = of_clk_get_by_name(dev->of_node, con_id); /* 从设备树节点得到clk */ if (!IS_ERR(clk)) /* 注意这里对错误判断取反了,即没错的话会直接返回clk */ return clk; if (PTR_ERR(clk) == -EPROBE_DEFER) /* 重新获取 */ return clk; } return clk_get_sys(dev_id, con_id); /* 设备树节点没找到,从系统注册链表中搜索得到 */ } struct clk *clk_get_sys(constchar *dev_id, constchar *con_id) { structclk_lookup *cl; mutex_lock(&clocks_mutex); cl = clk_find(dev_id, con_id); /* 根据设备名或链接名在clock链表中查找到对应的cl*/ if (cl && !__clk_get(cl->clk)) /* 这里对找到的时钟的引用计数+1 */ cl = NULL; mutex_unlock(&clocks_mutex); return cl ? cl->clk : ERR_PTR(-ENOENT); } /* * Find the correct struct clk for the device and connection ID. * We do slightly fuzzy matching here: * An entry with a NULL ID is assumed to be a wildcard. * If an entry has a device ID, it must match * If an entry has a connection ID, it must match * Then we take the most specific entry - with the following * order of precedence: dev+con > dev only > con only. 这是重点,即匹配优先级 */ static struct clk_lookup *clk_find(constchar *dev_id, constchar *con_id) { structclk_lookup *p, *cl = NULL; int match, best_found = 0, best_possible = 0; if (dev_id) /* 设备名称 */ best_possible += 2; if (con_id) /* 连接名称(可以是pclk,uart_clk,phy,pci,总是一般都是总线的时钟名称) */ best_possible += 1; list_for_each_entry(p, &clocks, node) { /* clocks链表中查找,根据dev+con > dev only > con优先级来匹配 */ match = 0; if (p->dev_id) { if (!dev_id || strcmp(p->dev_id, dev_id)) continue; match += 2; } if (p->con_id) { if (!con_id || strcmp(p->con_id, con_id)) continue; match += 1; } if (match > best_found) { cl = p; if (match != best_possible) best_found = match; else break; } } return cl; }
实现文件: drivers/clk/clk.c /** * clk_notifier_register: register a clock rate-change notifier callback * @clk: clock whose rate we are interested in * @nb: notifier block with callback function pointer * * ProTip: debugging across notifier chains can be frustrating. Make sure that * your notifier callback function prints a nice big warning in case of * failure. */ intclk_notifier_register(struct clk *clk, struct notifier_block *nb);
/** * clk_notifier_unregister: unregister a clock rate-change notifier callback * @clk: clock whose rate we are no longer interested in * @nb: notifier block which will be unregistered */ intclk_notifier_unregister(struct clk *clk, struct notifier_block *nb);
/** * 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. */ intof_clk_set_defaults(struct device_node *node, bool clk_supplier) { int rc;
if (!node) return0;
rc = __set_clk_parents(node, clk_supplier); if (rc < 0) return rc;