linux驱动-CCF-1 provider 注册时钟

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

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

乐分享-程序员

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值