CCF: common clock frameword
provider 注册时钟分析
1. 待注册 时钟数据
#define _REGISTER(f, s, ...) { .clk_register = (bcm2835_clk_register)f, \
.supported = s, \
.data = __VA_ARGS__ }
#define REGISTER_CLK(s, ...) _REGISTER(&bcm2835_register_clock, \
s, \
&(struct bcm2835_clock_data) \
{__VA_ARGS__})
1.外设时钟的parents(固定)
static const char *const bcm2835_clock_per_parents[] = {
"gnd",
"xosc",
"testdebug0",
"testdebug1",
"plla_per",
"pllc_per",
"plld_per",
"pllh_aux",
};
#define REGISTER_PER_CLK(s, ...) REGISTER_CLK( \
s, \
.num_mux_parents = ARRAY_SIZE(bcm2835_clock_per_parents), \
.parents = bcm2835_clock_per_parents, \
__VA_ARGS__)
2. 分析 dts 引用的时钟
serial@7e201000 {
compatible = "brcm,bcm2835-pl011\0arm,pl011\0arm,primecell";
reg = <0x7e201000 0x200>;
interrupts = <0x02 0x19>;
clocks = <0x03 0x13 0x03 0x14>;
clock-names = "uartclk\0apb_pclk";
arm,primecell-periphid = <0x241011>;
cts-event-workaround;
pinctrl-names = "default";
pinctrl-0 = <0x08 0x09>;
status = "okay";
phandle = <0x21>;
};分析[0x13=19] [0x16=20]
2.1 外设时钟-uart 配置
[BCM2835_CLOCK_UART] = REGISTER_PER_CLK(
SOC_ALL,
.name = "uart",
.ctl_reg = CM_UARTCTL,
.div_reg = CM_UARTDIV,
.int_bits = 10,
.frac_bits = 12,
.tcnt_mux = 28),
2.2 外设时钟-uart 配置 扩展结果
[19] = {
.clk_register = (bcm2835_clk_register)&bcm2835_register_clock,
.supported = ((1UL << (0)) | (1UL << (1))),
.data = &(struct bcm2835_clock_data)
{
.num_mux_parents = ARRAY_SIZE(bcm2835_clock_per_parents),
.parents = bcm2835_clock_per_parents,
.name = "uart",
.ctl_reg = 0x0f0,
.div_reg = 0x0f4,
.int_bits = 10,
.frac_bits = 12,
.tcnt_mux = 28
}
}
3. bcm2835_clk_probe
a. 调用bcm2835_register_clock 生成 struct clk_hw 数组
struct clk_hw 组成 struct clk_hw_onecell_data onecell
b. 调用 of_clk_add_hw_provider 根据 进一步生成of_clk_provider ,并添加到全局列表
struct clk_hw_onecell_data onecell 填充到struct of_clk_provider *cp 中的data
static int bcm2835_clk_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct clk_hw **hws;
struct bcm2835_cprman *cprman;
struct resource *res;
const struct bcm2835_clk_desc *desc;
const size_t asize = ARRAY_SIZE(clk_desc_array);
const struct cprman_plat_data *pdata;
struct device_node *fw_node;
size_t i;
u32 clk_id;
int ret;
pdata = of_device_get_match_data(&pdev->dev);
if (!pdata)
return -ENODEV;
cprman = devm_kzalloc(dev,
struct_size(cprman, onecell.hws, asize),
GFP_KERNEL);
........
platform_set_drvdata(pdev, cprman);
cprman->onecell.num = asize;
hws = cprman->onecell.hws;
for (i = 0; i < asize; i++) {
desc = &clk_desc_array[i];
if (desc->clk_register && desc->data &&
(desc->supported & pdata->soc)) {
hws[i] = desc->clk_register(cprman, desc->data);
}
}
......
ret = of_clk_add_hw_provider(dev->of_node, of_clk_hw_onecell_get,
&cprman->onecell);
return 0;
}
4. 关键数据结构
struct clk_hw_onecell_data {
unsigned int num;
struct clk_hw *hws[]; //provider 注册后的时钟数组
};
struct of_clk_provider {
struct list_head link;
struct device_node *node;
struct clk *(*get)(struct of_phandle_args *clkspec, void *data);
struct clk_hw *(*get_hw)(struct of_phandle_args *clkspec, void *data);
void *data; //对应 clk_hw_onecell_data(包含clk provider 注册的所有数组)
};
struct clk_hw {
struct clk_core *core;
struct clk *clk;
const struct clk_init_data *init;
};
struct clk_init_data {
const char *name;
const struct clk_ops *ops;
const char * const *parent_names;
u8 num_parents;
unsigned long flags;
};
struct clk_ops {
int (*prepare)(struct clk_hw *hw);
void (*unprepare)(struct clk_hw *hw);
int (*is_prepared)(struct clk_hw *hw);
void (*unprepare_unused)(struct clk_hw *hw);
int (*enable)(struct clk_hw *hw);
void (*disable)(struct clk_hw *hw);
int (*is_enabled)(struct clk_hw *hw);
void (*disable_unused)(struct clk_hw *hw);
unsigned long (*recalc_rate)(struct clk_hw *hw, unsigned long parent_rate);
// //通过查询硬件,重新计算此时钟的速率
long (*round_rate)(struct clk_hw *hw, unsigned long rate, unsigned long *parent_rate);
//给定目标速率作为输入,返回时钟实际支持的最接近速率
int (*determine_rate)(struct clk_hw *hw, struct clk_rate_request *req);
int (*set_parent)(struct clk_hw *hw, u8 index);
u8 (*get_parent)(struct clk_hw *hw);
int (*set_rate)(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate);
int (*set_rate_and_parent)(struct clk_hw *hw, rate, parent_rate, u8 index);
unsigned long (*recalc_accuracy)(struct clk_hw *hw, unsigned long parent_accuracy);
int (*get_phase)(struct clk_hw *hw);
int (*set_phase)(struct clk_hw *hw, int degrees);
int (*get_duty_cycle)(struct clk_hw *hw, struct clk_duty *duty);
int (*set_duty_cycle)(struct clk_hw *hw, struct clk_duty *duty);
void (*init)(struct clk_hw *hw);
void (*debug_init)(struct clk_hw *hw, struct dentry *dentry);
};
bcm2835_register_clock 追踪
static struct clk_hw *bcm2835_register_clock(struct bcm2835_cprman *cprman,
const struct bcm2835_clock_data *data)
{
struct bcm2835_clock *clock;
struct clk_init_data init;
const char *parents[1 << CM_SRC_BITS];
size_t i;
int ret;
/*
* Replace our strings referencing parent clocks with the
* actual clock-output-name of the parent.
*/
for (i = 0; i < data->num_mux_parents; i++) {
parents[i] = data->parents[i];
ret = match_string(cprman_parent_names,
ARRAY_SIZE(cprman_parent_names),
parents[i]);
if (ret >= 0)
parents[i] = cprman->real_parent_names[ret];
}
memset(&init, 0, sizeof(init));
init.parent_names = parents;
init.num_parents = data->num_mux_parents;
init.name = data->name;
init.flags = data->flags | CLK_IGNORE_UNUSED;
/*
* Some GPIO clocks for ethernet/wifi PLLs are marked as
* critical (since some platforms use them), but if the
* firmware didn't have them turned on then they clearly
* aren't actually critical.
*/
if ((cprman_read(cprman, data->ctl_reg) & CM_ENABLE) == 0)
init.flags &= ~CLK_IS_CRITICAL;
/*
* Pass the CLK_SET_RATE_PARENT flag if we are allowed to propagate
* rate changes on at least of the parents.
*/
if (data->set_rate_parent)
init.flags |= CLK_SET_RATE_PARENT;
if (data->is_vpu_clock) {
init.ops = &bcm2835_vpu_clock_clk_ops; //操作函数组
} else {
init.ops = &bcm2835_clock_clk_ops; //操作函数组
/* If the clock wasn't actually enabled at boot, it's not
* critical.
*/
if (!(cprman_read(cprman, data->ctl_reg) & CM_ENABLE))
init.flags &= ~CLK_IS_CRITICAL;
}
clock = devm_kzalloc(cprman->dev, sizeof(*clock), GFP_KERNEL);
if (!clock)
return NULL;
clock->cprman = cprman;
clock->data = data;
clock->hw.init = &init;
ret = devm_clk_hw_register(cprman->dev, &clock->hw);
if (ret)
return ERR_PTR(ret);
return &clock->hw;
}
devm_clk_hw_register
int devm_clk_hw_register(struct device *dev, struct clk_hw *hw)
{
struct clk_hw **hwp;
int ret;
hwp = devres_alloc(devm_clk_hw_release, sizeof(*hwp), GFP_KERNEL);
if (!hwp)
return -ENOMEM;
ret = clk_hw_register(dev, hw);
if (!ret) {
*hwp = hw;
devres_add(dev, hwp);
} else {
devres_free(hwp);
}
return ret;
}
clk_hw_register 与clk_register
int clk_hw_register(struct device *dev, struct clk_hw *hw)
{
return PTR_ERR_OR_ZERO(clk_register(dev, hw));
}
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;
}
if (WARN_ON(!hw->init->ops)) {
ret = -EINVAL;
goto fail_ops;
}
core->ops = hw->init->ops;
if (dev && pm_runtime_enabled(dev))
core->dev = dev;
if (dev && dev->driver)
core->owner = dev->driver->owner;
core->hw = hw; //hw与core 相互引用
core->flags = hw->init->flags;
core->num_parents = hw->init->num_parents;
core->min_rate = 0;
core->max_rate = ULONG_MAX;
hw->core = core; //hw与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);
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:
.....
}
__clk_core_init
if (core->ops->init)
core->ops->init(core->hw);if (core->ops->recalc_accuracy)
core->accuracy = core->ops->recalc_accuracy(core->hw,
__clk_get_accuracy(core->parent));
if (core->ops->get_phase)
core->phase = core->ops->get_phase(core->hw);
static int __clk_core_init(struct clk_core *core)
{
int i, ret;
struct clk_core *orphan;
struct hlist_node *tmp2;
unsigned long 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;
} 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;
}
/*
* 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));
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 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));
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;
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;
}
}
/*
* 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);
}
}
kref_init(&core->ref);
out:
clk_pm_runtime_put(core);
unlock:
if (ret)
hlist_del_init(&core->child_node);
clk_prepare_unlock();
if (!ret)
clk_debug_register(core);
return ret;
}