Linux clock driver(2) clk_register 详解

来源:互联网 发布:叶如何知秋 编辑:程序博客网 时间:2024/06/08 06:48

clk_register 详解

clk_register是底层clock driver用来向CCF(common clock framework)层注册时钟节点的接口,是CCF中的关键函数之一,下面将结合程序详细介绍其功能。

先看一下来看一下 clk_register 函数的实现:

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;                                                              }                                                                                    core->ops = hw->init->ops;                                                           if (dev && dev->driver)                                                                      core->owner = dev->driver->owner;                                            core->hw = hw;                                                                       core->flags = hw->init->flags;                                                       core->num_parents = hw->init->num_parents;                                           core->min_rate = 0;                                                                  core->max_rate = ULONG_MAX;                                                          hw->core = 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;                        }                                                   }                                                            /*创建哈希表头,每次调用get_clk接口都会创建一个clk实例,并将其加入此哈希表中,          注:每个clock由一个struct clk_core描述,其与struct clk_hw是一一对应的关系,          但是struct clk可能有很多个,其他驱动需要操作clock时,都需要先分配一个          struct clk 类型的指针,因此其与struct clk_core是一对多的关系,          也可以说clk是clk_core的实例*/                                                           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_parent_names_copy;                        }                                                           ret = __clk_init(dev, hw->clk);                             if (!ret)                                                           return hw->clk;                                     __clk_free_clk(hw->clk);                                    hw->clk = NULL;                                     fail_parent_names_copy:                                             while (--i >= 0)                                                    kfree_const(core->parent_names[i]);                 kfree(core->parent_names);                          fail_parent_names:                                                  kfree_const(core->name);                            fail_name:                                                          kfree(core);                                        fail_out:                                                           return ERR_PTR(ret);                                }                                                           EXPORT_SYMBOL_GPL(clk_register);                           

此函数的主要功能可分为3点:
1. 分配struct clk_core 类型的变量,并依据传入的hw指针对其进行初始化
2. 调用 __clk_create_clk 函数分配struct clk类型的变量
3. 调用__clk_init完成clock 的初始化

__clk_create_clk 函数的实现如下:

struct clk *__clk_create_clk(struct clk_hw *hw, const char *dev_id,                              const char *con_id)                    {                                                                           struct clk *clk;                                                    /* This is to allow this function to be chained to others */        if (!hw || IS_ERR(hw))                                                      return (struct clk *) hw;                                   clk = kzalloc(sizeof(*clk), GFP_KERNEL);   //分配struct clk类型的变量                         if (!clk)                                                                   return ERR_PTR(-ENOMEM);                                    clk->core = hw->core;                    //填充部分元素                                   clk->dev_id = dev_id;                                               clk->con_id = con_id;                                               clk->max_rate = ULONG_MAX;                                          clk_prepare_lock();                                                 hlist_add_head(&clk->clks_node, &hw->core->clks);   将此clk节点加入core->clks队列中                clk_prepare_unlock();                                               return clk;                                                 }                                                                   

__clk_init 函数实现如下:

static int __clk_init(struct device *dev, struct clk *clk_user)                                {                                                                                                      int i, ret = 0;                                                                                struct clk_core *orphan;                                                                       struct hlist_node *tmp2;                                                                       struct clk_core *core;                                                                         unsigned long rate;                                                                            if (!clk_user)                                                                                         return -EINVAL;                                                                        core = clk_user->core;                                                                         clk_prepare_lock();                                                                            /* 判断此clk是否已经注册过了 */                             if (clk_core_lookup(core->name)) {                                                                     pr_debug("%s: clk %s already initialized\n",                                                                   __func__, core->name);                                                         ret = -EEXIST;                                                                                 goto out;                                                                              }                                                                                              /* 判断底层clock的操作函数的设计是否符合规定.  可参考 Documentation/clk.txt */                                  if (core->ops->set_rate &&                                                                         !((core->ops->round_rate || core->ops->determine_rate) &&                                        core->ops->recalc_rate)) {                                                                       pr_warning("%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_warning("%s: %s must implement .get_parent & .set_parent\n",                                                __func__, core->name);                                                         ret = -EINVAL;                                                                                 goto out;                                                                              }                                                                                              if (core->ops->set_rate_and_parent &&                                                                          !(core->ops->set_parent && core->ops->set_rate)) {                                     pr_warn("%s: %s must implement .set_parent & .set_rate\n",                                                     __func__, core->name);                                                         ret = -EINVAL;                                                                                 goto out;                                                                              }                                                                                      /* 如果有parent name为null,则产生WARN */                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);                            /*                                                                         * Allocate an array of struct clk *'s to avoid unnecessary string         * look-ups of clk's possible parents.  This can fail for clocks passed    * in to clk_init during early boot; thus any access to core->parents[]    * must always check for a NULL pointer and try to populate it if          * necessary.                                                              *                                                                         * If core->parents is not NULL we skip this entire block.  This allows    * for clock drivers to statically initialize core->parents.               */                                                                       if (core->num_parents > 1 && !core->parents) {                                    core->parents = kcalloc(core->num_parents, sizeof(struct clk *),                                  GFP_KERNEL);                                      /*                                                                         * clk_core_lookup returns NULL for parents that have not been             * clk_init'd; thus any access to clk->parents[] must check                * for a NULL pointer.  We can always perform lazy lookups for             * missing parents later on.                                               */                                                                       if (core->parents)                                                                for (i = 0; i < core->num_parents; i++)                                           core->parents[i] =                                                                clk_core_lookup(core->parent_names[i]);   }                                                                         core->parent = __clk_init_parent(core);  /*判断此节点是否为孤儿节点,如果有parent且parent已经初始化,则其延续父节点的特性,如果为ROOT节点,就肯定不是孤儿节点了,否则为孤儿节点*/if (core->parent) {                                                 hlist_add_head(&core->child_node,                                           &core->parent->children);                   core->orphan = core->parent->orphan;                } else if (core->flags & CLK_IS_ROOT) {                             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;                                }                                                           /*此处应该是设置精度,但是从我们的底层驱动看,这个都是设置为默认值0*/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;                                         /*设置相位,默认为0*/     if (core->ops->get_phase)                                     core->phase = core->ops->get_phase(core->hw); else                                                          core->phase = 0; /*设置时钟频率,如果实现了recalc_rate函数,则通过此函数获取,如果有parent,则和parent频率一致,否则设置为0*/                              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; /*查看当前结构是否为某些孤儿节点的parent,如果是,则reparent*/                            hlist_for_each_entry_safe(orphan, tmp2, &clk_orphan_list, child_node) {        if (orphan->num_parents && orphan->ops->get_parent) {                          i = orphan->ops->get_parent(orphan->hw);                               if (i >= 0 && i < orphan->num_parents &&                                   !strcmp(core->name, orphan->parent_names[i]))                              clk_core_reparent(orphan, core);                               continue;                                                      }                                                                     /*如果没有实现get_parent,则遍历parents列表*/                                                                        for (i = 0; i < orphan->num_parents; i++)                                      if (!strcmp(core->name, orphan->parent_names[i])) {                            clk_core_reparent(orphan, core);                                       break;                                                         }                                                       }            /*如无特殊硬件层面的限制,不建议实现此init函数*/                                                               if (core->ops->init)                             core->ops->init(core->hw);           kref_init(&core->ref);               out:                                                 clk_prepare_unlock();                        if (!ret)                                            clk_debug_register(core);            return ret;                          }                                           
原创粉丝点击