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

ops/2024/10/19 9:36:52/

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;elsecore->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);elsecore->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;elserate = 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;
}


http://www.ppmy.cn/ops/25309.html

相关文章

Redis(七) zset有序集合类型

文章目录 前言命令ZADDZCARDZCOUNTZRANGEZREVRANGEZRANGEBYSCOREZPOPMAXZPOPMIN两个阻塞版本的POP命令BZPOPMAX BZPOPMINZRANKZREVRANKZSCOREZREMZREMRANGEBYRANKZREMRANGEBYSCOREZINCRBY集合间操作ZINTERSTOREZUNIONSTORE 命令小结 内部编码使用场景 前言 对于有序集合这个名…

Big Data 平障录

Hive Hive 生成带压缩的格式&#xff0c;需要如此设置 SET parquet.compressionSNAPPY;yarn.scheduler.fair.assignmultiple 相关jira&#xff1a;https://issues.apache.org/jira/browse/YARN-5035 yarn.scheduler.fair.assignmultiple是YARN Fair Scheduler的一个配置参数…

vue 配合 video.js 实现视频播放

1. 导入 video.js 包 npm install video.js -S npm install videojs-flash -S 2. 代码实现 <template><div><videoid"my-video"class"video-js"controlspreload"auto"width"640"height"264":poster&quo…

Linux基础——Linux开发工具(上)vim

前言&#xff1a;在了解完Linux基本指令和Linux权限后&#xff0c;我们有了足够了能力来学习后面的内容&#xff0c;但是在真正进入Linux之前&#xff0c;我们还得要学会使用Linux中的几个开发工具。而我们主要介绍的是以下几个&#xff1a; yum, vim, gcc / g, gdb, make / ma…

大型语言模型LLM的数据管理与应用

大型语言模型&#xff08;LLM&#xff09;风靡全球&#xff0c;尤其是 OpenAI 的最新发展。LLMs 的魅力来自于其理解、解释和生成人类语言的能力&#xff0c;而这曾被认为是人类的专属领域。像 CoPilot 这样的工具正在迅速融入开发人员的日常生活&#xff0c;而以 ChatGPT 为动…

stm32单片机开发一、中断之外部中断实验

stm32单片机的外部中断和定时器中断、ADC中断等都由stm32的内核中的NVIC模块控制&#xff0c;stm32的中断有很多中&#xff0c;比如供电不足中断&#xff0c;当供电不足时&#xff0c;会产生的一种中断&#xff0c;这么多中断如果都接在CPU上&#xff0c;或者说CPU去处理&#…

Ieetcode——21.合并两个有序链表

21. 合并两个有序链表 - 力扣&#xff08;LeetCode&#xff09; 合并两个有序链表我们的思路是创建一个新链表&#xff0c;然后遍历已知的两个有序链表&#xff0c;并比较其节点的val值&#xff0c;将小的尾插到新链表中&#xff0c;然后继续遍历&#xff0c;直到将该两个链表…

【C/C++】动态内存管理(C:malloc,realloc,calloc,free || C++:new,delete)

&#x1f525;个人主页&#xff1a; Forcible Bug Maker &#x1f525;专栏&#xff1a; C | | C语言 目录 前言C/C内存分布C语言中的动态内存管理&#xff1a;malloc/realloc/realloc/freemallocrealloccallocfree C中的动态内存管理&#xff1a;new/deletenew和delete操作内…