Linux clock driver(2) clk_register 详解

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

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值