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;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.mzph.cn/news/829857.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Vscode——SSH连接不进去服务器的万能解决办法

一、查看当前版本VSCode的commit_id Help -> About -> Commit&#xff08;对应中文版本&#xff1a;帮助 -> 关于 -> 提交&#xff09; 会得到一串数字字母&#xff0c;我们简称 ID。 二、手动下载对应的VSCode包 浏览器输入&#xff1a;https://update.code.v…

手撕红黑树(kv模型模拟)

目录 前言 一、相关概念 二、性质介绍 红黑树平衡说明 三、红黑树模拟&#xff08;kv结构&#xff09; 1、红黑树节点 2、红黑树插入 2、特殊处理情况 声明&#xff1a; 情况一&#xff1a;cur为红&#xff0c;p为红&#xff0c;g为黑&#xff0c;u存在&#xff0c;且…

【MHA】MySQL高可用MHA介绍3-命令详解

目录 masterha_manager&#xff1a;运行 MHA Manager 的命令 通用参数 监控特定参数 故障转移特定参数 masterha_master_switch 手动故障转移 非交互式故障转移 计划&#xff08;在线&#xff09;主切换 masterha_check_status masterha_check_repl masterha_stop mas…

Spring Cloud学习笔记(Feign):配置类(未完成)

这是本人学习的总结&#xff0c;主要学习资料如下 - 马士兵教育 1、给Feign配置的方式1.1、通过Bean配置1.2、application.yaml配置 2、配置日志2.1、日志级别1.2、指定日志级别1.2.1、通过Bean配置1.2.2、application.yaml配置 3、Inteceptor配置 1、给Feign配置的方式 我们有…

视觉生成范式 VAR: Visual Auto Regressive

使 GPT 风格的自回归模型在图像生成首次超越扩散模型&#xff0c;并观察到与大语言模型相似的 Scaling Laws 缩放定律、Zero-shot Task Generalization 泛化能力 体验网站&#xff1a;https://var.vision/ 论文链接&#xff1a;https://arxiv.org/abs/2404.02905 开源代码&a…

牛客NC99 多叉树的直径【较难 深度优先 Java/Go/PHP】

题目 题目链接&#xff1a; https://www.nowcoder.com/practice/a77b4f3d84bf4a7891519ffee9376df3 思路 核心就是树的最大直径(globalMax)一定是以某一个node为root最长的两个path-to-leaf. 就是普通dfs的同时算路径长度。时间: O(n), DFS一次 空间: O(n)参考答案Java impo…

通过this.$router.push跳转同一路由页面只会刷新一次的问题怎么解决

一、原因 Vue Router默认会认为同一路由之间的跳转是相同路由的导航&#xff0c;所以不会触发组件的重新渲染。 当我们连续重复跳转相同的路由导航时就会报错如&#xff1a; Uncaught (in promise) NavigationDuplicated: Avoided redundant navigation to current location…

在ArcGIS中,矢量数据有.shp,.mdb和.gdb,为啥建议使用gdb?

在ArcGIS中,矢量数据可以存储在多种格式中,如 .shp (Shapefile)、.mdb (Microsoft Access Database) 和 .gdb (Geodatabase)。每种格式都有其特定的用途和优缺点,但通常推荐使用 Geodatabase(.gdb)格式,原因如下: 1. 更高的数据容量和性能 容量: Shapefiles 和 MDB 文…

有关钱包相关开发的库和依赖

Trezor 钱包 GitHub 组织&#xff1a;https://github.com/trezor说明&#xff1a;Trezor 是一款硬件加密货币钱包&#xff0c;它的团队开发了与助记词相关的许多工具和库。 Electrum 钱包 GitHub 仓库&#xff1a;https://github.com/spesmilo/electrum说明&#xff1a;Electru…

Pytorch中保存模型的两种方法

目录 一、保存整个模型二、只保存模型参数 一、保存整个模型 这种方法会保存模型的整个定义&#xff08;包括网络结构和参数&#xff09;。这样保存的模型可以直接被加载并使用&#xff0c;但前提是保存和加载模型的代码环境需要完全一致。 保存模型时使用代码&#xff1a; …

stm32的GPIO基本结构

1.带FT标号的引脚能容忍5V 2.GPIO系统架构 stm32的所有GPIO都是挂载在APB2总线上的 3.GPIO的基本结构 在上图中&#xff0c;左边就是寄存器&#xff0c;右边就是驱动器了 保护二极管的作用&#xff1a;VDD表示3.3V&#xff0c;如果输入的电压的值大于3.3V&#xff0c;那么这个…

企业级OV SSL证书,主要应用在哪些场景

我们来看看OV SSL证书的一个典型应用&#xff0c;即电子商务网站。随着电子商务的发展&#xff0c;网上购物已经成为人们日常生活的一部分。然而&#xff0c;这同时也带来了一个问题&#xff0c;就是用户在进行网上交易时&#xff0c;如何保证其个人信息、银行卡信息等敏感数据…

就业班 第三阶段(nginx) 2401--4.26 day5 nginx5 nginx https部署实战

三、HTTPS 基本原理 1、https 介绍 HTTPS&#xff08;全称&#xff1a;HyperText Transfer Protocol over Secure Socket Layer&#xff09;&#xff0c;其实 HTTPS 并不是一个新鲜协议&#xff0c;Google 很早就开始启用了&#xff0c;初衷是为了保证数据安全。 近些年&…

BMP JPG PNG 介绍以及三者区别

BMP&#xff08;Bitmap&#xff09;、JPG&#xff08;Joint Photographic Experts Group&#xff09;和PNG&#xff08;Portable Network Graphics&#xff09;是三种常用的图像文件格式&#xff0c;各自具有独特的特性和应用场景。 下面是它们的详细介绍和区别&#xff1a; B…

免费实用在线小工具

免费在线工具 https://orcc.online/ pdf在线免费转word文档 https://orcc.online/pdf 时间戳转换 https://orcc.online/timestamp Base64 编码解码 https://orcc.online/base64 URL 编码解码 https://orcc.online/url Hash(MD5/SHA1/SHA256…) 计算 https://orcc.online/ha…

基于地图的平滑算法--佛洛依德路径平滑算法(FLOYD)

系列文章目录 提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加 TODO:写完再整理 文章目录 系列文章目录前言基于地图的平滑算法--佛洛依德路径平滑算法(FLOYD)前言 认知有限,望大家多多包涵,有什么问题也希望能够与大家多交流,共同成长! 本文先对*…

Python 数据库基类封装

目录 1.BaseEntity 2.t_user 3.DAOBase 4.UserDAO 刚开始用Python做Api后端&#xff0c;记录下学习过程 从数据库操作开始&#xff0c;定义实体&#xff0c;定义DAO层基类&#xff0c;及DAO业务类的使用 写的不好&#xff0c;有好的设计建议欢迎大家提出&#xff0c;一起…

Python 使用相对路径读取文件失败

python open一个问及那时使用绝对路径可以&#xff0c;但是使用相对路径时报错&#xff0c;找不到指定文件 解决步骤如下&#xff1a; 添加Python配置 在新增的配置Json文件添加下图红框这一行

知网怎么查重 知网查重的详细步骤

知网查重八个步骤&#xff1a;1. 访问官网&#xff0c;注册账号。2. 上传待查文档。3. 选择查重规则。4. 选择相似来源库。5. 提交查重任务。6. 等待查重结果。7. 获取查重报告。8. 下载查重报告。 知网查重的详细步骤 第一步&#xff1a;进入知网查重系统 打开浏览器&#x…

27.统一网关Gateway-路由断言工厂

在配置文件中写的断言规则只是字符串&#xff0c;这些字符串会被Predicate Factory读取并处理&#xff0c;转变为路由判断的条件。 例如&#xff1a;Path /user/** 是按照路劲匹配&#xff0c;这个规则是由 org.springframework.cloud.gateway.handler.predicate.PathRouteP…