1. 程式人生 > >Linux clock driver(2) clk_register 詳解

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