当前位置: 首页 > news >正文

如何做网站内容架构分析成都网站建设天府软件园

如何做网站内容架构分析,成都网站建设天府软件园,常州营销型网站建设,深圳人力资源网求职目录 前言CCF 介绍提供者和消费者的概念CCF 框架组成关系CCF 程序关键结构体 CCF 重要组成注册时钟未使用设备树的时钟注册操作使用设备树的时钟注册操作设备树分析与使用clocks 分析举例时钟输出的名称of_parse_phandle_with_args 的示例说明__of_clk_get_from_provider 的分析… 目录 前言CCF 介绍提供者和消费者的概念CCF 框架组成关系CCF 程序关键结构体 CCF 重要组成注册时钟未使用设备树的时钟注册操作使用设备树的时钟注册操作设备树分析与使用clocks 分析举例时钟输出的名称of_parse_phandle_with_args 的示例说明__of_clk_get_from_provider 的分析CCF 时钟获取机制的总结 编写时钟提供者驱动实际使用示例 前言 linux 内核版本 v4.19 嵌入式平台rv1109 , 文中代码出处。 CCF 介绍 提供者和消费者的概念 CCF背后的主要思想是统一和抽象分布在不同SoC时钟驱动程序中的类似代码。这种标准化的方法引入了时钟提供者和时钟消费者的概念: 提供者是Linux内核驱动程序它连接到框架并提供对硬件的访问从而根据SoC数据表提供(使这些对消费者可用)时钟树(由于可以转储整个时钟树); 消费者是通过公共API访问框架的Linux内核驱动程序或子系统; 也就是说驱动程序既可以是提供者也可以是消费者(然后它可以使用它提供的一个或多个时钟也可以使用其他人提供的一个或多个时钟)。 CCF 框架组成关系 在使用CCF之前需要通过CONFIG_COMMON_CLK选项将其支持加入内核CCF 本身分为两部分 公共时钟框架核心:这是框架的核心当您添加新的驱动程序并提供struct clk的公共定义时不应该修改它它统一了框架级代码和传统的依赖于平台的实现这些实现过去常常在各种平台上复制。这一半还允许我们将消费者接口(也称为clk实现)包装在结构体clk_ops之上该结构体必须由每个时钟提供程序提供。 特定于硬件的那一半:它的目标是必须为每个新的硬件时钟写入的时钟设备。这需要驱动程序提供clk_ops结构体该结构体对应于用于对底层硬件进行操作的回调函数(这些回调函数由时钟的核心实现调用)以及包装和抽象时钟硬件的相应硬件特定结构。 这两部分通过struct clk_hw连接在一起。 CCF 程序关键结构体 struct clk_hw是CCF中每种时钟类型的基本结构。它可以看作是一个句柄用于从struct clk遍历到相应的特定于硬件的结构。 include/linux/clk-provider.h struct clk_hw {struct clk_core *core;struct clk *clk; //时钟的消费者表示每个消费者API都依赖于这个结构体。const struct clk_init_data *init; };clk 它由时钟框架分配和维护并在需要时提供给时钟使用者。每当消费者通过clk_get启动对CCF中的时钟设备(即clk_core)的访问时它都需要获得一个句柄即clk. init 在初始化底层时钟提供程序驱动程序的过程中调用clk_register()接口来注册时钟硬件。在此之前需要设置一些初始数据这些初始数据被抽象为struct clk_init_data数据结构。在初始化过程中clk_init_data中的数据用于初始化clk_core数据结构该数据结构对应于clk_hw。初始化完成后clk_init_data没有任何意义。 CCF 重要组成 注册时钟 struct clk *clk_register(struct device *dev, struct clk_hw *hw) int clk_hw_register(struct device *dev, struct clk_hw *hw)调用clk_hw_register()(它在内部调用__clk_core_init()来初始化时钟)时如果这个时钟有一个有效的父时钟它将在父时钟的子列表中结束。另一方面如果num_parent为0则将其放在clk_root_list中。否则它将挂起在clk_orphan_list中这意味着它没有有效的父节点。此外每当一个新的时钟被clk_init时CCF将遍历clk_orphan_list(孤儿时钟列表)并重新父化当前正在初始化的时钟的子时钟。这就是CCF保持时钟树与硬件拓扑一致的方式。另一方面struct clk是时钟设备的消费者端实例。 基本上所有用户对时钟设备的访问都会创建一个结构clk类型的访问句柄。当不同的用户访问相同的时钟设备时尽管在底层使用相同的struct clk_core实例但他们访问的句柄(struct clk)是不同的。 clk_hw_register 封装了clk_register只是为了兼容推荐使用clk_hw_register (不应该直接使用clk_reregister因为clk_reregister返回结构clk。这可能会导致混乱并打破提供者和使用者接口之间的严格分离)。 clk_hw_register / clk_register 的实现逻辑如下clk/clk.c 代码略 分配struct clk_core空间clk_hw-core 根据struct clk_hw指针提供的信息初始化clk的字段名称、ops、hw、flags、num_parents和parents_name。调用内核接口__clk_core_init来执行后续初始化操作包括构建时钟树层次结构。 通过内部内核接口clk_create_clk分配struct clk空间clk_hw-clk并返回此结构clk变量。 CCF框架负责建立整个抽象时钟树的树结构并维护其数据因此它通过drivers/clk/clk.c中定义的两个静态链表来实现这一点如下所示 static HLIST_HEAD(clk_root_list); static HLIST_HEAD(clk_orphan_list);每当您在时钟hw上调用clk_hw_register()它在内部调用__clk_core_int来初始化时钟时如果该时钟有一个有效的父级它将最终出现在父级的子级列表中。另一方面若num_parent为0则将其放置在clk_root_list中。否则它将挂在clk_orpan_list中这意味着它没有有效的父级。此外每次新的clk为clk_init时CCF都会遍历clk_orpan_list孤立时钟的列表并为当前正在初始化的时钟的子级重新设置父级。这就是CCF保持时钟树与硬件拓扑一致的方式。 未使用设备树的时钟注册操作 由于知道clk_register的目的只是注册到公共时钟框架因此消费者无法知道如何定位clk。因此对于底层时钟提供程序驱动程序除了调用clk_register函数以注册到公共时钟框架之外还必须在clk_register之后立即调用clk_register_clkdev以便用名称绑定时钟否则时钟使用者将不知道如何定位时钟。因此内核使用struct clk_lookup顾名思义来查找可用的时钟。为了使用基于hw的API强制实现提供者和使用者代码之间的分离代码中的clk_hw_register_clkdev和clk_register_clkdev。 clk_lookup 结构 struct clk_lookup {struct list_head node;const char *dev_id;const char *con_id;struct clk *clk;struct clk_hw *clk_hw; };dev_id和con_id用于识别/查找适当的clk。这个clk是相应的底层时钟。node是挂在全局时钟列表中 clk_hw_register_clkdev -- _clkdev_add static void __clkdev_add(struct clk_lookup *cl) {mutex_lock(clocks_mutex);list_add_tail(cl-node, clocks);mutex_unlock(clocks_mutex); }void clkdev_add(struct clk_lookup *cl) {if (!cl-clk_hw)cl-clk_hw __clk_get_hw(cl-clk);__clkdev_add(cl); } EXPORT_SYMBOL(clkdev_add);使用设备树的时钟注册操作 使用设备树后每个时钟提供程序都成为DTS中的一个节点也就是说每个clk在其对应的设备树中都有一个设备节点。在这种情况下与其将clk和名称绑定在一起不如通过一个新的数据结构struct of_clk_provider来绑定clk和你的设备节点。具体数据结构如下 struct of_clk_provider {struct list_head link;//Entry in global list of clock providersstruct device_node *node;//表示时钟设备的DTS节点struct clk *(*get)(struct of_phandle_args *clkspec, void *data);struct clk_hw *(*get_hw)(struct of_phandle_args *clkspec, void *data);void *data; };of_clk_provider 注释中的 “Entry” 有 “条目账目记录” 的意思可理解为条目。 get_hw是时钟的回调。对于设备使用者通过clk_get() 调用它来返回与节点相关联的时钟或NULL。这里后会代码会讲解 get 为老的API兼容老的驱动代码与get_hw功能一样。 CCF 引入了一个新的list 帮助管理所有DTS节点和时钟之间的对应关系 static LIST_HEAD(of_clk_providers);of_clk_add_hw_provider 代替 clk_hw_register_clkdev /*** of_clk_add_hw_provider() - Register a clock provider for a node* np: Device node pointer associated with clock provider* get: callback for decoding clk_hw* data: context pointer for get callback.*/ int of_clk_add_hw_provider(struct device_node *np,struct clk_hw *(*get)(struct of_phandle_args *clkspec,void *data),void *data)这在后面的例子中会看到of_clk_add_hw_provider 的使用 设备树分析与使用 clocks 分析举例 ti芯片的cdce706 为例 clocks {clk54: clk54 {#clock-cells 0;compatible fixed-clock;clock-frequency 54000000;clock-output-names osc;};};i2c0: i2c-master0d090000 {......cdce706: clock-synth69 {compatible ti,cdce706;#clock-cells 1;reg 0x69;clocks clk54;clock-names clk_in0;};};时钟是通过clocks属性分配给使用者的时钟提供者也可以是消费者。clk54是一个固定时钟cdce706是一个时钟提供者它也使用clk54在clocks属性中作为phandle给出。 时钟提供程序节点需要指定的最重要的信息是 #clock-cells 属性它决定了时钟说明符的长度 当它为0时这意味着只需要将该提供程序的phandle属性提供给使用者。当它为1或更大时这意味着phandle属性具有多个输出并且需要提供附加信息例如指示需要使用什么输出的ID。此ID直接由立即值表示。最好在头文件中定义系统中所有时钟的ID。设备树可以包括这个头文件例如clocksclock CLK_SPI0其中CLK_SPI0是在头文件中定义的宏。 时钟输出的名称 让我们来看看时钟输出名称。这是一个可选但推荐的属性应该是与输出即提供的时钟线名称相对应的字符串列表 osc {#clock-cells 1;clock-output-names ckout1, ckout2; //注意这里的clock-output-names output 名字固定表明输出时钟 };定义了一个设备该设备提供两条时钟输出线分别命名为ckout1和ckout2 消费者的节点不应直接使用这些clout1 等名称来引用这些时钟线应该使用适应的时钟说明符即 提供者的#clock-cells 根据设备的需求命名输入时钟线如下 device {clocks osc 0, osc 1; clock-names baud, register; //消费者时钟名clock-names };当一条时钟线被分配给一个消费者设备时当该消费者的驱动程序调用clk_get或用于获取时钟的类似接口时该接口调用of_clk_get_by_name后者反过来调用__of_clk_get。 static struct clk *__of_clk_get(struct device_node *np, int index,const char *dev_id, const char *con_id) {struct of_phandle_args clkspec;struct clk *clk;int rc;rc of_parse_phandle_with_args(np, clocks, #clock-cells, index,clkspec);if (rc)return ERR_PTR(rc);clk __of_clk_get_from_provider(clkspec, dev_id, con_id, true);of_node_put(clkspec.np);return clk; }关注 一下of_parse_phandle_with_args 函数中的clkspec //of.h #define MAX_PHANDLE_ARGS 16 struct of_phandle_args {struct device_node *np;int args_count;uint32_t args[MAX_PHANDLE_ARGS]; };在struct of_phandle_args中np元素是指向与phandle属性相对应的节点的指针。在时钟说明符中它将是时钟提供程序的设备树节点。args_count元素对应于说明符中phandle后面的单元格数。它可以用于遍历argsargs是一个包含有内容的参数的数组。 of_parse_phandle_with_args 的示例说明 上面的文字描述难以说明白下面用代码举例说明of_parse_phandle_with_args phandle1: node1 {#gpio-cells 2; };phandle2: node2 {#list-cells 1; };node3 {list phandle1 1 2 phandle2 3; };//或者下面的写法 node3 {list phandle1 1 2, phandle2 3; }这里node3是一个消费者。要获得指向node2节点的设备节点指针可以调用of_parse_phandle_with_args(node3 ‘list’‘#list-cells’1args)。由于phandle2位于列表(list)中的索引1从0开始因此我们在index参数中指定了1。 同样要获取node1节点的关联设备节点可以调用of_parse_phandle_with_argsnode3‘list’‘#gpio-cells’0args。对于第二种情况如果我们查看args输出参数我们将看到args-np对应于node3args-args_count的值为2因为这个说明符需要2个参数args–args[0]的值为1args-args[1]的值为2中这将对应于说明符中的2个参数。 __of_clk_get_from_provider 的分析 对于of_parse_phandle_with_args 的理解到这里接下来看一下 __of_clk_get_from_provider struct clk *__of_clk_get_from_provider(struct of_phandle_args *clkspec,const char *dev_id, const char *con_id,bool with_orphans) {struct of_clk_provider *provider;struct clk *clk ERR_PTR(-EPROBE_DEFER);struct clk_hw *hw;if (!clkspec)return ERR_PTR(-EINVAL);/* Check if we have such a provider in our array */mutex_lock(of_clk_mutex);list_for_each_entry(provider, of_clk_providers, link) {if (provider-node clkspec-np) {hw __of_clk_get_hw_from_provider(provider, clkspec);clk __clk_create_clk(hw, dev_id, con_id,with_orphans);}if (!IS_ERR(clk)) {if (!__clk_get(clk)) {__clk_free_clk(clk);clk ERR_PTR(-ENOENT);}break;}}mutex_unlock(of_clk_mutex);return clk; }这个函数只是遍历时钟提供程序在of_clk_providers列表中当找到合适的提供程序时它会调用作为of_clk_add_provider的第二个参数给定的底层回调以解码底层时钟。这里of_parse_phandle_with_args返回的时钟说明符作为参数给出上文中的of_phandle_args 结构体参数。当你必须向其他设备公开时钟提供程序时我们不得不使用_clk_add_hw_provider。作为第二个参数每当使用者调用clk_get时该接口接受CCF用来解码底层时钟的回调。 此回调的结构如下中的clk_src_get 回调 int of_clk_add_provider(struct device_node *np,struct clk *(*clk_src_get)(struct of_phandle_args *clkspec,void *data),void *data)CCF 时钟获取机制的总结 当消费者调用clk_get()时CCF内部调用__of_clk_get(struct device_node *np, int index,const char *dev_id, const char *con_id)。这是作为该使用者的设备节点属性的第一个参数给出的因此CCF可以获取时钟说明符并找到与提供程序对应的设备节点特性通过of_parse_phandle_with_args() 。然后它以of_phandle_args的形式返回这个值。这个of_phandle_args对应于时钟说明符并作为参数提供给__of_clk_ get_from_provider()该参数只是将of_phandle_args即of_phandler_args-np中提供程序的设备节点属性与of_clk_provider中存在的属性进行比较后者是设备树时钟提供程序的列表。一旦找到匹配就会调用该提供程序的相应of_clk_provider-get回调并返回底层时钟。 尽管可以编写自己的回调但CCF框架提供了两个通用的解码回调涵盖了大多数情况。它们分别是of_clk_src_onecell_get() 和of_clk_src_simple_get() of_clk_hw_simple_get() 用于简单的时钟提供程序其中除了时钟本身之外不需要特殊的上下文数据结构例如时钟gpio驱动程序在drivers/clk/clk-gpio.c中。 struct clk_hw *of_clk_hw_simple_get(struct of_phandle_args *clkspec, void *data) {return data; } EXPORT_SYMBOL_GPL(of_clk_hw_simple_get);of_clk_src_onecell_get 的分析略 编写时钟提供者驱动 略… 后面的文章中再写 实际使用示例 前面提到的 of_clk_add_hw_provider 使用示例下面为rk809 pmic 中clk的部分代码 struct rk808_clkout {struct rk808 *rk808;struct clk_hw clkout1_hw;struct clk_hw clkout2_hw; };static struct clk_hw * of_clk_rk808_get(struct of_phandle_args *clkspec, void *data) {struct rk808_clkout *rk808_clkout data;unsigned int idx clkspec-args[0];if (idx 2) {pr_err(%s: invalid index %u\n, __func__, idx);return ERR_PTR(-EINVAL);}return idx ? rk808_clkout-clkout2_hw : rk808_clkout-clkout1_hw; }static int rk808_clkout_probe(struct platform_device *pdev) {struct rk808 *rk808 dev_get_drvdata(pdev-dev.parent);struct i2c_client *client rk808-i2c;struct device_node *node client-dev.of_node;struct clk_init_data init {};struct rk808_clkout *rk808_clkout;int ret;rk808_clkout devm_kzalloc(client-dev,sizeof(*rk808_clkout), GFP_KERNEL);if (!rk808_clkout)return -ENOMEM;rk808_clkout-rk808 rk808;init.parent_names NULL;init.num_parents 0;init.name rk808-clkout1;init.ops rk808_clkout1_ops;rk808_clkout-clkout1_hw.init init;/* optional override of the clockname */of_property_read_string_index(node, clock-output-names,0, init.name);ret devm_clk_hw_register(client-dev, rk808_clkout-clkout1_hw);if (ret)return ret;init.name rk808-clkout2;init.ops rkpmic_get_ops(rk808-variant);rk808_clkout-clkout2_hw.init init;/* optional override of the clockname */of_property_read_string_index(node, clock-output-names,1, init.name);ret devm_clk_hw_register(client-dev, rk808_clkout-clkout2_hw);if (ret)return ret;return of_clk_add_hw_provider(node, of_clk_rk808_get, rk808_clkout); }static int rk808_clkout_remove(struct platform_device *pdev) {struct rk808 *rk808 dev_get_drvdata(pdev-dev.parent);struct i2c_client *client rk808-i2c;struct device_node *node client-dev.of_node;of_clk_del_provider(node);return 0; }static struct platform_driver rk808_clkout_driver {.probe rk808_clkout_probe,.remove rk808_clkout_remove,.driver {.name rk808-clkout,}, };module_platform_driver(rk808_clkout_driver);
http://www.pierceye.com/news/301521/

相关文章:

  • 商城网站设计企业建一个o2o网站
  • 营销型网站创建php源码资源网
  • 优秀设计师网站在上海做兼职去哪个网站搜索
  • 教育行业网站建设房屋3d立体设计软件
  • 海珠免费网站建设php网站系统
  • 建设银行网站信息补充施工企业奖惩制度范本
  • 做拍卖网站多少钱办公室装修铺哪种地板
  • 国外网站有哪些平台网站系统建设需要什么资质
  • 做网站 就上凡科网做盗版网站会坐牢吗
  • 百事通网做网站做网站公众号多少钱
  • 影视作品网站开发与设计php做网站有哪些好处
  • 寻模板网站源码《基层建设》官方网站
  • 做网站龙岗深圳市网络seo推广平台
  • 公司资质查询官方网站网站建设开题报告
  • 桐城网站开发网站制作器手机版下载
  • 校友网站 建设网站开发都是使用框架吗
  • 手机网站字体大小规范优秀网站
  • 用动物做网站名称建设个人网站的策划书
  • 深圳网站开发公司宝网建设网站需要哪些素材
  • 网页设计需要学什么学历南宁seo服务公司
  • 网站开发学习未来做那个网站能致富
  • 建设厅科技中心网站免费网站怎么做出来的
  • 做一网站东莞网站建设
  • 电商网站建设需求网站开发培训排名
  • 厦门建设局网站城市建设郑州百度分公司
  • 长春火车站在哪广州冼村小学
  • 网站开发上海工资dedecms模板自适应
  • 湖北建设厅行政服务中心网站网络系统管理员工作内容
  • php 创建网站开发织梦网站后台打不开
  • 网站建设的企业html 网站