1. 程式人生 > >從零開始之驅動發開、linux驅動(三十八、Linux common clock framework(3)_實現邏輯分析)

從零開始之驅動發開、linux驅動(三十八、Linux common clock framework(3)_實現邏輯分析)

1. 前言

前面兩篇clock framework的分析文章,分別從clock consumerclock provider的角度,介紹了Linux kernel怎麼管理系統的clock資源,以及device driver怎麼使用clock資源。本文將深入到clock framework的內部,分析相關的實現邏輯。

注:本文使用的kernel版本為linux-3.16.57。雖然最新版本的kernel增加了一些內容,但主要邏輯沒有改變,就不緊跟kernel的步伐了。

2. struct clk結構

到目前為止,我們還沒有仔細介紹過struct clk這個代表了一個clock的資料結構呢。對consumer和provider來說,可以不關心,但對內部實現邏輯來說,就不得不提了:


struct clk {
	const char		*name;
	const struct clk_ops	*ops;
	struct clk_hw		*hw;
	struct module		*owner;
	struct clk		*parent;
	const char		**parent_names;
	struct clk		**parents;
	u8			num_parents;
	u8			new_parent_index;
	unsigned long		rate;
	unsigned long		new_rate;
	struct clk		*new_parent;
	struct clk		*new_child;
	unsigned long		flags;
	unsigned int		enable_count;
	unsigned int		prepare_count;
	unsigned long		accuracy;
	struct hlist_head	children;
	struct hlist_node	child_node;
	unsigned int		notifier_count;
#ifdef CONFIG_DEBUG_FS
	struct dentry		*dentry;
#endif
	struct kref		ref;
};

 

name, ops, hw, parents_name, num_parents, flags, 可參考在上一節已經說過;

parent,儲存了該clock當前的parent clock的struct clk指標;

parents,一個指標陣列,儲存了所有可能的parent clock的struct clk指標;

rate,當前的clock rate;

new_rate,新設定的clock rate,之所要儲存在這裡,是因為set rate過程中有一些中間計算,後面再詳解;

enable_count, prepare_count,該clock被enable和prepare的次數,用於確保enable/disable以及prepare/unprepare的成對呼叫;

children,該clock的children clocks(孩兒們),以連結串列的形式組織;

child_node,一個list node,自己作為child時,掛到parent的children list時使用;

notifier_count,記錄註冊到notifier的個數。

3.  clock regitser/unregister

在上一節中分析過,clock provider需要將系統的clock以tree的形式組織起來,分門別類,並在系統初始化時,通過provider的初始化介面,或者clock framework core的DTS介面,將所有的clock註冊到kernel。

clock的註冊,統一由clk_regitser介面實現,但基於該介面,kernel也提供了其它更為便利註冊介面,下面將會一一描述。

3.1 clk_regitser

clk_register是所有register介面的共同實現,負責將clock註冊到kernel,並返回代表該clock的struct clk指標。分析該介面之前,我們先看一下下面的內容:

上面是kernel中clk_register介面可能的實現位置,由此可以看出,clk_register在“include/linux/clk-provider.h”中宣告,卻可能在不同的C檔案中實現。其它clock API也類似。這說明了什麼?

這恰恰呼應了“Linux common clock framework”中“common”一詞。

在舊的kernel中,clock framework只是規定了一系列的API宣告,具體API的實現,由各個machine程式碼完成。這就導致每個machine目錄下,都有一個類似clock.c的檔案,以比較相似的邏輯,實現clock provider的功能。顯然,這裡面有很多冗餘程式碼。

後來,kernel將這些公共程式碼,以clock provider的形式(上面drivers/clk/clk.c檔案)抽象出來,就成了我們所說的common clock framework。

後面所有的描述,都會以common clock  framework的核心程式碼為基礎,其它的,就不再涉及了。

下面是clk_register的實現:


/**
 * clk_register - allocate a new clock, register it and return an opaque cookie
 * @dev: device that is registering this clock
 * @hw: link to hardware-specific clock data
 *
 * clk_register is the primary interface for populating the clock tree with new
 * clock nodes.  It returns a pointer to the newly allocated struct clk which
 * cannot be dereferenced by driver code but may be used in conjuction with the
 * rest of the clock API.  In the event of an error clk_register will return an
 * error code; drivers must test for an error code after calling clk_register.
 */
struct clk *clk_register(struct device *dev, struct clk_hw *hw)
{
	int i, ret;
	struct clk *clk;

	clk = kzalloc(sizeof(*clk), GFP_KERNEL);            /* 申請一個clk */
	if (!clk) {
		pr_err("%s: could not allocate clk\n", __func__);
		ret = -ENOMEM;
		goto fail_out;
	}

	clk->name = kstrdup(hw->init->name, GFP_KERNEL);    /* 把hw裡面的name重新拷貝一份給clk的name */
	if (!clk->name) {
		pr_err("%s: could not allocate clk->name\n", __func__);
		ret = -ENOMEM;
		goto fail_name;
	}
	clk->ops = hw->init->ops;                /* clk繫結ops */
	if (dev && dev->driver)
		clk->owner = dev->driver->owner;
	clk->hw = hw;                            /* clk繫結hw */
	clk->flags = hw->init->flags;
	clk->num_parents = hw->init->num_parents;    /* clk繫結mux的所有partnt */
	hw->clk = clk;                        /* hw繫結clk */

    /* 給num個parent申請num個指標,用來儲存所有的paerent */
	/* allocate local copy in case parent_names is __initdata */
	clk->parent_names = kcalloc(clk->num_parents, sizeof(char *),
					GFP_KERNEL);

	if (!clk->parent_names) {
		pr_err("%s: could not allocate clk->parent_names\n", __func__);
		ret = -ENOMEM;
		goto fail_parent_names;
	}


    /* 把hw中所有的parent的name都重新拷貝一份繫結到新申請的clk上 */
	/* copy each string name in case parent_names is __initdata */
	for (i = 0; i < clk->num_parents; i++) {
		clk->parent_names[i] = kstrdup(hw->init->parent_names[i],
						GFP_KERNEL);
		if (!clk->parent_names[i]) {
			pr_err("%s: could not copy parent_names\n", __func__);
			ret = -ENOMEM;
			goto fail_parent_names_copy;
		}
	}

	ret = __clk_init(dev, clk);        /* 校驗fops,執行init函式 */
	if (!ret)
		return clk;

fail_parent_names_copy:
	while (--i >= 0)
		kfree(clk->parent_names[i]);
	kfree(clk->parent_names);
fail_parent_names:
	kfree(clk->name);
fail_name:
	kfree(clk);
fail_out:
	return ERR_PTR(ret);
}

該介面接受一個struct clk_hw指標,該指標包含了將要註冊的clock的資訊(具體可參考上一節),在內部分配一個struct clk變數後,將clock資訊儲存在變數中,並返回給呼叫者。實現邏輯如下:

分配struct clk空間;

根據struct clk_hw指標提供的資訊,初始化clk的name、ops、hw、flags、num_parents、parents_names等變數; 

呼叫內部介面__clk_init,執行後續的初始化操作。這個介面包含了clk_regitser的主要邏輯,具體如下。 

 

/**
 * __clk_init - initialize the data structures in a struct clk
 * @dev:	device initializing this clk, placeholder for now
 * @clk:	clk being initialized
 *
 * Initializes the lists in struct clk, queries the hardware for the
 * parent and rate and sets them both.
 */
int __clk_init(struct device *dev, struct clk *clk)
{
	int i, ret = 0;
	struct clk *orphan;
	struct hlist_node *tmp2;

	if (!clk)
		return -EINVAL;

	clk_prepare_lock();         //獲取鎖

	/* check to see if a clock with this name is already registered */
    /*校驗當前註冊的時鐘是否已經註冊過了*/
	if (__clk_lookup(clk->name)) {
		pr_debug("%s: clk %s already initialized\n",
				__func__, clk->name);
		ret = -EEXIST;
		goto out;
	}

	/* check that clk_ops are sane.  See Documentation/clk.txt */
    /* 檢查ops裡面是否有相應clk的計算函式 */
	if (clk->ops->set_rate &&
	    !((clk->ops->round_rate || clk->ops->determine_rate) &&
	      clk->ops->recalc_rate)) {
		pr_warning("%s: %s must implement .round_rate or .determine_rate in addition to .recalc_rate\n",
				__func__, clk->name);
		ret = -EINVAL;
		goto out;
	}

    /* 校驗該clk的ops裡面是否由設定和得到當前時鐘的介面 */
	if (clk->ops->set_parent && !clk->ops->get_parent) {
		pr_warning("%s: %s must implement .get_parent & .set_parent\n",
				__func__, clk->name);
		ret = -EINVAL;
		goto out;
	}

    /* 校驗如果有set_rate_and_parent ,則set_parent和set_rate也必須有,因為裡面呼叫這兩個了  */
	if (clk->ops->set_rate_and_parent &&
			!(clk->ops->set_parent && clk->ops->set_rate)) {
		pr_warn("%s: %s must implement .set_parent & .set_rate\n",
				__func__, clk->name);
		ret = -EINVAL;
		goto out;
	}

	/* throw a WARN if any entries in parent_names are NULL */
    /* 檢查parent不是NULL */
	for (i = 0; i < clk->num_parents; i++)
		WARN(!clk->parent_names[i],
				"%s: invalid NULL in %s's .parent_names\n",
				__func__, clk->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 clk->parents[]
	 * must always check for a NULL pointer and try to populate it if
	 * necessary.
	 *
	 * If clk->parents is not NULL we skip this entire block.  This allows
	 * for clock drivers to statically initialize clk->parents.
	 */
    /* 如果父時鐘個數大於1個,且目前沒繫結好paernt,則查詢到並繫結到clk的parents中 */
	if (clk->num_parents > 1 && !clk->parents) {
		clk->parents = kcalloc(clk->num_parents, sizeof(struct clk *),
					GFP_KERNEL);
		/*
		 * __clk_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.
		 */
        /* 從clk tree中根據parent_names中的每個name查詢parent,並繫結到clk->parents陣列中 */
		if (clk->parents)
			for (i = 0; i < clk->num_parents; i++)
				clk->parents[i] =
					__clk_lookup(clk->parent_names[i]);
	}

	clk->parent = __clk_init_parent(clk);    /* 得到parent */

	/*
	 * Populate clk->parent if parent has already been __clk_init'd.  If
	 * parent has not yet been __clk_init'd then place clk in the orphan
	 * list.  If clk has set the CLK_IS_ROOT flag 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 (clk->parent)        
		hlist_add_head(&clk->child_node,
				&clk->parent->children);
	else if (clk->flags & CLK_IS_ROOT)
		hlist_add_head(&clk->child_node, &clk_root_list);    
	else
		hlist_add_head(&clk->child_node, &clk_orphan_list);

	/*
	 * 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).
	 */
    //設定clk的準確性
	if (clk->ops->recalc_accuracy)
		clk->accuracy = clk->ops->recalc_accuracy(clk->hw,
					__clk_get_accuracy(clk->parent));
	else if (clk->parent)
		clk->accuracy = clk->parent->accuracy;
	else
		clk->accuracy = 0;

	/*
	 * 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 (clk->ops->recalc_rate)
		clk->rate = clk->ops->recalc_rate(clk->hw,
				__clk_get_rate(clk->parent));
	else if (clk->parent)
		clk->rate = clk->parent->rate;
	else
		clk->rate = 0;

	clk_debug_register(clk);
	/*
	 * walk the list of orphan clocks and reparent any that are children of
	 * this clock
	 */
    //遍歷孤兒時鐘列表並重新顯示此時鐘的任何孩子
	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 (!strcmp(clk->name, orphan->parent_names[i]))
				__clk_reparent(orphan, clk);
			continue;
		}

		for (i = 0; i < orphan->num_parents; i++)
			if (!strcmp(clk->name, orphan->parent_names[i])) {
				__clk_reparent(orphan, clk);
				break;
			}
	 }

	/*
	 * 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.
	 */
    //如果clock ops提供了init介面,執行之(由註釋可知,kernel不建議提供init介面)。
	if (clk->ops->init)    
		clk->ops->init(clk->hw);

	kref_init(&clk->ref);
out:
	clk_prepare_unlock();

	return ret;
}

__clk_init介面的實現相當繁瑣,做的事情包括:

21~27行,以clock name為引數,呼叫__clk_lookup介面,查詢是否已有相同name的clock註冊,如果有,則返回錯誤。由此可以看出,clock framework以name唯一識別一個clock,因此不能有同名的clock存在;

29~55行,檢查clk ops的完整性,例如:如果提供了set_rate介面,就必須提供round_rate和recalc_rate介面;如果提供了set_parent,就必須提供get_parent。這些邏輯背後的含義,會在後面相應的地方詳細描述;

57~89行,分配一個struct clk *型別的陣列,快取該clock的parents clock。具體方法是根據parents_name,查詢相應的struct clk指標;

91行,獲取當前的parent clock,並將其儲存在parent指標中。具體可參考下面“說明2”;

93~110行,根據該clock的特性,將它新增到clk_root_list、clk_orphan_list或者parent->children三個連結串列中的一個,具體請參考下面“說明1”;

112~126行,計算時鐘的準確性

118~141行,計算clock的初始rate,具體請參考下面“說明3”;

148~162行,嘗試reparent當前所有的孤兒(orphan)clock,具體請參考下面“說明4”;

164~174行,如果clock ops提供了init介面,執行之(由註釋可知,kernel不建議提供init介面)。

 上面的clock init流程,有下面4點補充說明:

說明1:clock的管理和查詢

static HLIST_HEAD(clk_root_list);
static HLIST_HEAD(clk_orphan_list);
static LIST_HEAD(clk_notifier_list);

clock framework有2條全域性的連結串列:clk_root_list和clk_orphan_list。所有設定了CLK_IS_ROOT屬性的clock都會掛在clk_root_list中。其它clock,如果有valid的parent ,則會掛到parent的“children”連結串列中,如果沒有valid的parent,則會掛到clk_orphan_list中。

查詢時(__clk_lookup介面做的事情),依次搜尋:clk_root_list-->root_clk-->children-->child's children,clk_orphan_list-->orphan_clk-->children-->child's children,即可。

說明2:當前parent clock的選擇(__clk_init_parent)

對於沒有parent,或者只有1個parent 的clock來說,比較簡單,設定為NULL,或者根據parent name獲得parent的struct clk指標接。

對於有多個parent的clock,就必須提供.get_parent ops,該ops要根據當前硬體的配置情況,例如暫存器值,返回當前所有使用的parent的index(即第幾個parent)。然後根據index,取出對應parent clock的struct clk指標,作為當前的parent。

說明3:clock的初始rate計算

對於提供.recalc_rate ops的clock來說,優先使用該ops獲取初始的rate。如果沒有提供,退而求其次,直接使用parent clock的rate。最後,如果該clock沒有parent,則初始的rate只能選擇為0。

.recalc_rate ops的功能,是以parent clock的rate為輸入引數,根據當前硬體的配置情況,如暫存器值,計算獲得自身的rate值。

說明4:orphan clocks的reparen 

有些情況下,child clock會先於parent clock註冊,此時該child就會成為orphan clock,被收養在clk_orphan_list中。

而每當新的clock註冊時,kernel都會檢查這個clock是否是某個orphan的parent,如果是,就把這個orphan從clk_orphan_list中移除,放到新註冊的clock的懷抱。這就是reparent的功能,它的處理邏輯是:

a) 遍歷orphan list,如果orphan提供了.get_parent ops,則通過該ops得到當前parent的index,並從parent_names中取出該parent的name,然後和新註冊的clock name比較,如果相同,呵呵,找到parent了,執行__clk_reparent,進行後續的操作。

b) 如果沒有提供.get_parent ops,只能遍歷自己的parent_names,檢查是否有和新註冊clock匹配的,如果有,執行__clk_reparent,進行後續的操作。

c) __clk_reparent會把這個orphan從clk_orphan_list中移除,並掛到新註冊的clock上。然後呼叫__clk_recalc_rates,重新計算自己以及自己所有children的rate。計算過程和上面的clock rate設定類似。

3.2 clk_unregister/devm_clk_register/devm_clk_unregister

clock的regitser和init,幾乎佔了clock framework大部分的實現邏輯。clk_unregister是regitser介面的反操作。而devm_clk_register/devm_clk_unregister則是clk_register/clk_unregister的device resource manager版本。


/**
 * clk_unregister - unregister a currently registered clock
 * @clk: clock to unregister
 */
void clk_unregister(struct clk *clk)
{
	unsigned long flags;

       if (!clk || WARN_ON_ONCE(IS_ERR(clk)))
               return;

	clk_prepare_lock();

	if (clk->ops == &clk_nodrv_ops) {    /* 檢查是否已經解除安裝的 */
		pr_err("%s: unregistered clock: %s\n", __func__, clk->name);
		goto out;
	}
	/*
	 * Assign empty clock ops for consumers that might still hold
	 * a reference to this clock.
	 */
	flags = clk_enable_lock();
	clk->ops = &clk_nodrv_ops;        /* 已經解除安裝的會用空的drv來標記 */
	clk_enable_unlock(flags);

	if (!hlist_empty(&clk->children)) {        /* 如果有子clk,則該clk解除安裝後,它的所有子clk都要設定成孤兒 */
		struct clk *child;
		struct hlist_node *t;

		/* Reparent all children to the orphan list. */
		hlist_for_each_entry_safe(child, t, &clk->children, child_node)
			clk_set_parent(child, NULL);
	}

	clk_debug_unregister(clk);

	hlist_del_init(&clk->child_node);        /* 刪除 */

	if (clk->prepare_count)
		pr_warn("%s: unregistering prepared clock: %s\n",
					__func__, clk->name);

	kref_put(&clk->ref, __clk_release);
out:
	clk_prepare_unlock();
}

 


/**
 * devm_clk_register - resource managed clk_register()
 * @dev: device that is registering this clock
 * @hw: link to hardware-specific clock data
 *
 * Managed clk_register(). Clocks returned from this function are
 * automatically clk_unregister()ed on driver detach. See clk_register() for
 * more information.
 */
struct clk *devm_clk_register(struct device *dev, struct clk_hw *hw)
{
	struct clk *clk;
	struct clk **clkp;

	clkp = devres_alloc(devm_clk_release, sizeof(*clkp), GFP_KERNEL);
	if (!clkp)
		return ERR_PTR(-ENOMEM);

	clk = clk_register(dev, hw);
	if (!IS_ERR(clk)) {
		*clkp = clk;
		devres_add(dev, clkp);
	} else {
		devres_free(clkp);
	}

	return clk;
}

/**
 * devm_clk_unregister - resource managed clk_unregister()
 * @clk: clock to unregister
 *
 * Deallocate a clock allocated with devm_clk_register(). Normally
 * this function will not need to be called and the resource management
 * code will ensure that the resource is freed.
 */
void devm_clk_unregister(struct device *dev, struct clk *clk)
{
	WARN_ON(devres_release(dev, devm_clk_release, devm_clk_match, clk));
}


static void devm_clk_release(struct device *dev, void *res)
{
	clk_unregister(*(struct clk **)res);
}

3.3 fixed rate clock的註冊

上一節中已經對fixed rate clock有過詳細的介紹,這種型別的clock有兩種註冊方式,通過API註冊和通過DTS註冊,具體的實現位於“drivers/clk/clk-fixed-rate.c”中,介紹如下。

/**
 * clk_register_fixed_rate - register fixed-rate clock with the clock framework
 * @dev: device that is registering this clock
 * @name: name of this clock
 * @parent_name: name of clock's parent
 * @flags: framework-specific flags
 * @fixed_rate: non-adjustable clock rate
 */
//註冊固定時鐘頻率
struct clk *clk_register_fixed_rate(struct device *dev, const char *name,
		const char *parent_name, unsigned long flags,
		unsigned long fixed_rate)
{
	return clk_register_fixed_rate_with_accuracy(dev, name, parent_name,
						     flags, fixed_rate, 0);
}


/**
 * clk_register_fixed_rate_with_accuracy - register fixed-rate clock with the
 *					   clock framework
 * @dev: device that is registering this clock
 * @name: name of this clock
 * @parent_name: name of clock's parent
 * @flags: framework-specific flags
 * @fixed_rate: non-adjustable clock rate
 * @fixed_accuracy: non-adjustable clock rate
 */
struct clk *clk_register_fixed_rate_with_accuracy(struct device *dev,
		const char *name, const char *parent_name, unsigned long flags,
		unsigned long fixed_rate, unsigned long fixed_accuracy)
{
	struct clk_fixed_rate *fixed;
	struct clk *clk;
	struct clk_init_data init;

	/* allocate fixed-rate clock */
	fixed = kzalloc(sizeof(struct clk_fixed_rate), GFP_KERNEL);    //分配一個固定係數的時鐘
	if (!fixed) {
		pr_err("%s: could not allocate fixed clk\n", __func__);
		return ERR_PTR(-ENOMEM);
	}

	init.name = name;
	init.ops = &clk_fixed_rate_ops;        /* 繫結固定時鐘的ops,見下面 */
	init.flags = flags | CLK_IS_BASIC;
	init.parent_names = (parent_name ? &parent_name: NULL);       /* 這種只有一個parent或沒有 */
	init.num_parents = (parent_name ? 1 : 0);

	/* struct clk_fixed_rate assignments */
	fixed->fixed_rate = fixed_rate;        /* 固定速率 */
	fixed->fixed_accuracy = fixed_accuracy;    /* 固定精度 */
	fixed->hw.init = &init;

	/* register the clock */
	clk = clk_register(dev, &fixed->hw);    /* 註冊固定評論的clk */
	if (IS_ERR(clk))
		kfree(fixed);

	return clk;
}


說明1:struct clk_init_data型別的變數

struct clk_init_data型別的變數(init),是一個區域性變數,傳遞給clk_regitser使用時,用的是它的指標,說明了什麼?說明該變數不會再後面使用了。再回憶一下clk_regitser的實現邏輯,會把所有的資訊copy一遍,這裡就好理解了。後面其它型別的clock註冊時,道理相同。

說明2:fixed rate clock的實現思路

私有資料結構的定義如下:

/**
 * struct clk_fixed_rate - fixed-rate clock
 * @hw:		handle between common and hardware-specific interfaces
 * @fixed_rate:	constant frequency of clock
 */
struct clk_fixed_rate {
	struct		clk_hw hw;
	unsigned long	fixed_rate;
	unsigned long	fixed_accuracy;
	u8		flags;
};

包含一個struct clk_hw變數,用於clk_regitser。另外三個變數,則為該型別clock特有的屬性。私有資料結構變數(fixed)是通過kzalloc分配的,說明後續還需要使用。那怎麼用呢?

由clk_regitser的實現可知,fixed rate clock註冊時hw);>,把fixed指標中hw變數的地址儲存在了struct clk指標中了。因此,在任何時候,通過struct clk指標(clock的代表),就可以找到所對應clock的struct clk_hw指標,從而可以找到相應的私有變數(fixed)的指標以及其中的私有資料。

基於此,fixed rate ops的實現就順利成章了:


/*
 * DOC: basic fixed-rate clock that cannot gate
 *
 * Traits of this clock:
 * prepare - clk_(un)prepare only ensures parents are prepared
 * enable - clk_enable only ensures parents are enabled
 * rate - rate is always a fixed value.  No clk_set_rate support
 * parent - fixed parent.  No clk_set_parent support
 */

#define to_clk_fixed_rate(_hw) container_of(_hw, struct clk_fixed_rate, hw)

static unsigned long clk_fixed_rate_recalc_rate(struct clk_hw *hw,
		unsigned long parent_rate)
{
	return to_clk_fixed_rate(hw)->fixed_rate;    /* 固定時鐘頻率 */
}

static unsigned long clk_fixed_rate_recalc_accuracy(struct clk_hw *hw,
		unsigned long parent_accuracy)
{
	return to_clk_fixed_rate(hw)->fixed_accuracy;    /* 固定時鐘精度 */
}

const struct clk_ops clk_fixed_rate_ops = {
	.recalc_rate = clk_fixed_rate_recalc_rate,
	.recalc_accuracy = clk_fixed_rate_recalc_accuracy,
};

2)通過DTS註冊

fixed rate clock是非常簡單的一種clock,因而可以直接通過DTS的形式註冊,clock framework負責解析DTS,並呼叫API註冊clock,如下:

#ifdef CONFIG_OF
/**
 * of_fixed_clk_setup() - Setup function for simple fixed rate clock
 */
void of_fixed_clk_setup(struct device_node *node)
{
	struct clk *clk;
	const char *clk_name = node->name;
	u32 rate;
	u32 accuracy = 0;

	if (of_property_read_u32(node, "clock-frequency", &rate))
		return;

	of_property_read_u32(node, "clock-accuracy", &accuracy);

	of_property_read_string(node, "clock-output-names", &clk_name);

	clk = clk_register_fixed_rate_with_accuracy(NULL, clk_name, NULL,
						    CLK_IS_ROOT, rate,
						    accuracy);
	if (!IS_ERR(clk))
		of_clk_add_provider(node, of_clk_src_simple_get, clk);
}
EXPORT_SYMBOL_GPL(of_fixed_clk_setup);
CLK_OF_DECLARE(fixed_clk, "fixed-clock", of_fixed_clk_setup);
#endif

 

#define CLK_OF_DECLARE(name, compat, fn) OF_DECLARE_1(clk, name, compat, fn)


#define OF_DECLARE_1(table, name, compat, fn) \
		_OF_DECLARE(table, name, compat, fn, of_init_fn_1)


#ifdef CONFIG_OF
#define _OF_DECLARE(table, name, compat, fn, fn_type)			\
	static const struct of_device_id __of_table_##name		\
		__used __section(__##table##_of_table)			\
		 = { .compatible = compat,				\
		     .data = (fn == (fn_type)NULL) ? fn : fn  }
#else
#define _OF_DECLARE(table, name, compat, fn, fn_type)					\
	static const struct of_device_id __of_table_##name		\
		__attribute__((unused))					\
		 = { .compatible = compat,				\
		     .data = (fn == (fn_type)NULL) ? fn : fn }
#endif

首先看一下CLK_OF_DECLARE巨集,它的定義位於“include/linux/clk-provider.h”中,負責在指定的section中(以__clk_of_table開始的位置),定義struct of_device_id型別的變數,並由of_clk_init介面解析、匹配,如果匹配成功,則執行相應的回撥函式(這裡為of_fixed_clk_setup);

初始化的時候,device tree負責讀取DTS,並和這些變數的名字(這裡為"fixed-clock")匹配,如果匹配成功,則執行相應的回撥函式(這裡為of_fixed_clk_setup);

of_fixed_clk_setup會解析兩個DTS欄位"clock-frequency"和"clock-output-names",然後呼叫clk_register_fixed_rate,註冊clock。注意,註冊時的flags為CLK_IS_ROOT,說明目前只支援ROOT型別的clock通過DTS註冊;

最後,呼叫of_clk_add_provider介面,將該clock新增到provider list中,方便後續的查詢使用。該介面會在後面再詳細介紹。

 of_clk_init負責從DTS中掃描並初始化clock provider,如下:


/**
 * of_clk_init() - Scan and init clock providers from the DT
 * @matches: array of compatible values and init functions for providers.
 *
 * This function scans the device tree for matching clock providers
 * and calls their initialization functions. It also does it by trying
 * to follow the dependencies.
 */
void __init of_clk_init(const struct of_device_id *matches)
{
	const struct of_device_id *match;
	struct device_node *np;
	struct clock_provider *clk_provider, *next;
	bool is_init_done;
	bool force = false;

	if (!matches)
		matches = &__clk_of_table;

	/* First prepare the list of the clocks providers */
	for_each_matching_node_and_match(np, matches, &match) {
		struct clock_provider *parent =
			kzalloc(sizeof(struct clock_provider),	GFP_KERNEL);

		parent->clk_init_cb = match->data;
		parent->np = np;
		list_add_tail(&parent->node, &clk_provider_list);
	}

	while (!list_empty(&clk_provider_list)) {
		is_init_done = false;
		list_for_each_entry_safe(clk_provider, next,
					&clk_provider_list, node) {
			if (force || parent_ready(clk_provider->np)) {
				clk_provider->clk_init_cb(clk_provider->np);
				list_del(&clk_provider->node);
				kfree(clk_provider);
				is_init_done = true;
			}
		}

		/*
		 * We didn't manage to initialize any of the
		 * remaining providers during the last loop, so now we
		 * initialize all the remaining ones unconditionally
		 * in case the clock parent was not mandatory
		 */
		if (!is_init_done)
			force = true;

	}
}

該介面有一個輸入引數,用於指定需要掃描的OF id,如果留空,則會掃描__clk_of_table,就是通過CLK_OF_DECLARE巨集指定的fixed rate等型別的clock。

在最新的kernel中,會在初始化程式碼(time_init)中以NULL為引數呼叫一次of_clk_init,以便自動匹配並初始化DTS中的描述的類似fixed rate的clock。

這裡使用大量篇幅描述一個簡單的fixed rate clock的註冊方式,主要目的是給大家介紹一種通用的實現方式,或者說通用思路。後面其它型別的clock,包括我們自定義的型別,實現方法都是一樣的。這裡就不羅嗦了,大家看程式碼就可以了。

3.4 gate、devider、mux、fixed factor、composite以及自定義型別clock的註冊

這裡先放有一個時鐘分頻器(devider)的驅動底層實現

/**
 * clk_register_divider - register a divider clock with the clock framework
 * @dev: device registering this clock
 * @name: name of this clock
 * @parent_name: name of clock's parent
 * @flags: framework-specific flags
 * @reg: register address to adjust divider
 * @shift: number of bits to shift the bitfield
 * @width: width of the bitfield
 * @clk_divider_flags: divider-specific flags for this clock
 * @lock: shared register lock for this clock
 */
struct clk *clk_register_divider(struct device *dev, const char *name,
		const char *parent_name, unsigned long flags,
		void __iomem *reg, u8 shift, u8 width,
		u8 clk_divider_flags, spinlock_t *lock)
{
	return _register_divider(dev, name, parent_name, flags, reg, shift,
			width, clk_divider_flags, NULL, lock);
}
EXPORT_SYMBOL_GPL(clk_register_divider);

/**
 * clk_register_divider_table - register a table based divider clock with
 * the clock framework
 * @dev: device registering this clock
 * @name: name of this clock
 * @parent_name: name of clock's parent
 * @flags: framework-specific flags
 * @reg: register address to adjust divider
 * @shift: number of bits to shift the bitfield
 * @width: width of the bitfield
 * @clk_divider_flags: divider-specific flags for this clock
 * @table: array of divider/value pairs ending with a div set to 0
 * @lock: shared register lock for this clock
 */
struct clk *clk_register_divider_table(struct device *dev, const char *name,
		const char *parent_name, unsigned long flags,
		void __iomem *reg, u8 shift, u8 width,
		u8 clk_divider_flags, const struct clk_div_table *table,
		spinlock_t *lock)
{
	return _register_divider(dev, name, parent_name, flags, reg, shift,
			width, clk_divider_flags, table, lock);
}

 


static struct clk *_register_divider(struct device *dev, const char *name,
		const char *parent_name, unsigned long flags,
		void __iomem *reg, u8 shift, u8 width,
		u8 clk_divider_flags, const struct clk_div_table *table,
		spinlock_t *lock)
{
	struct clk_divider *div;
	struct clk *clk;
	struct clk_init_data init;

	if (clk_divider_flags & CLK_DIVIDER_HIWORD_MASK) {
		if (width + shift > 16) {
			pr_warn("divider value exceeds LOWORD field\n");
			return ERR_PTR(-EINVAL);
		}
	}

	/* allocate the divider */
	div = kzalloc(sizeof(struct clk_divider), GFP_KERNEL);
	if (!div) {
		pr_err("%s: could not allocate divider clk\n", __func__);
		return ERR_PTR(-ENOMEM);
	}

	init.name = name;
	init.ops = &clk_divider_ops;        /* 分頻器操作介面 */
	init.flags = flags | CLK_IS_BASIC;
	init.parent_names = (parent_name ? &parent_name: NULL);
	init.num_parents = (parent_name ? 1 : 0);

	/* struct clk_divider assignments */
	div->reg = reg;            /* 分頻器暫存器地址 */
	div->shift = shift;        /* 分頻器從那一位開始 */
	div->width = width;        /* 分頻器位寬 */
	div->flags = clk_divider_flags;
	div->lock = lock;
	div->hw.init = &init;
	div->table = table;

	/* register the clock */
	clk = clk_register(dev, &div->hw);

	if (IS_ERR(clk))
		kfree(div);

	return clk;
}

因為分頻器分頻可能涉及到不止2分頻,還可能1、2、4...很多分頻的情況,註冊時會註冊對應值以及分頻比,同時也會有讀寫暫存器,以及按需求設定最接近要求的頻率的計算和設定,需要設定某個頻率,最終是通過查表來確認分頻值,所以分頻器屬於比較複雜的時鐘,程式碼會比較多,但思路和固定頻率的完全一樣。


/*
 * DOC: basic adjustable divider clock that cannot gate
 *
 * Traits of this clock:
 * prepare - clk_prepare only ensures that parents are prepared
 * enable - clk_enable only ensures that parents are enabled
 * rate - rate is adjustable.  clk->rate = DIV_ROUND_UP(parent->rate / divisor)
 * parent - fixed parent.  No clk_set_parent support
 */

#define to_clk_divider(_hw) container_of(_hw, struct clk_divider, hw)

#define div_mask(d)	((1 << ((d)->width)) - 1)

static unsigned int _get_table_maxdiv(const struct clk_div_table *table)
{
	unsigned int maxdiv = 0;
	const struct clk_div_table *clkt;

	for (clkt = table; clkt->div; clkt++)
		if (clkt->div > maxdiv)
			maxdiv = clkt->div;
	return maxdiv;
}

static unsigned int _get_table_mindiv(const struct clk_div_table *table)
{
	unsigned int mindiv = UINT_MAX;
	const struct clk_div_table *clkt;

	for (clkt = table; clkt->div; clkt++)
		if (clkt->div < mindiv)
			mindiv = clkt->div;
	return mindiv;
}

static unsigned int _get_maxdiv(struct clk_divider *divider)
{
	if (divider->flags & CLK_DIVIDER_ONE_BASED)
		return div_mask(divider);
	if (divider->flags & CLK_DIVIDER_POWER_OF_TWO)
		return 1 << div_mask(divider);
	if (divider->table)
		return _get_table_maxdiv(divider->table);
	return div_mask(divider) + 1;
}

static unsigned int _get_table_div(const struct clk_div_table *table,
							unsigned int val)
{
	const struct clk_div_table *clkt;

	for (clkt = table; clkt->div; clkt++)
		if (clkt->val == val)
			return clkt->div;
	return 0;
}

static unsigned int _get_div(struct clk_divider *divider, unsigned int val)
{
	if (divider->flags & CLK_DIVIDER_ONE_BASED)
		return val;
	if (divider->flags & CLK_DIVIDER_POWER_OF_TWO)
		return 1 << val;
	if (divider->table)
		return _get_table_div(divider->table, val);
	return val + 1;
}

static unsigned int _get_table_val(const struct clk_div_table *table,
							unsigned int div)
{
	const struct clk_div_table *clkt;

	for (clkt = table; clkt->div; clkt++)
		if (clkt->div == div)
			return clkt->val;
	return 0;
}

static unsigned int _get_val(struct clk_divider *divider, unsigned int div)
{
	if (divider->flags & CLK_DIVIDER_ONE_BASED)
		return div;
	if (divider->flags & CLK_DIVIDER_POWER_OF_TWO)
		return __ffs(div);
	if (divider->table)
		return  _get_table_val(divider->table, div);
	return div - 1;
}

static unsigned long clk_divider_recalc_rate(struct clk_hw *hw,
		unsigned long parent_rate)
{
	struct clk_divider *divider = to_clk_divider(hw);
	unsigned int div, val;

	val = clk_readl(divider->reg) >> divider->shift;
	val &= div_mask(divider);

	div = _get_div(divider, val);
	if (!div) {
		WARN(!(divider->flags & CLK_DIVIDER_ALLOW_ZERO),
			"%s: Zero divisor and CLK_DIVIDER_ALLOW_ZERO not set\n",
			__clk_get_name(hw->clk));
		return parent_rate;
	}

	return DIV_ROUND_UP(parent_rate, div);
}

static bool _is_valid_table_div(const struct clk_div_table *table,
							 unsigned int div)
{
	const struct clk_div_table *clkt;

	for (clkt = table; clkt->div; clkt++)
		if (clkt->div == div)
			return true;
	return false;
}

static bool _is_valid_div(struct clk_divider *divider, unsigned int div)
{
	if (divider->flags & CLK_DIVIDER_POWER_OF_TWO)
		return is_power_of_2(div);
	if (divider->table)
		return _is_valid_table_div(divider->table, div);
	return true;
}

static int _round_up_table(const struct clk_div_table *table, int div)
{
	const struct clk_div_table *clkt;
	int up = INT_MAX;

	for (clkt = table; clkt->div; clkt++) {
		if (clkt->div == div)
			return clkt->div;
		else if (clkt->div < div)
			continue;

		if ((clkt->div - div) < (up - div))
			up = clkt->div;
	}

	return up;
}

static int _round_down_table(const struct clk_div_table *table, int div)
{
	const struct clk_div_table *clkt;
	int down = _get_table_mindiv(table);

	for (clkt = table; clkt->div; clkt++) {
		if (clkt->div == div)
			return clkt->div;
		else if (clkt->div > div)
			continue;

		if ((div - clkt->div) < (div - down))
			down = clkt->div;
	}

	return down;
}

static int _div_round_up(struct clk_divider *divider,
		unsigned long parent_rate, unsigned long rate)
{
	int div = DIV_ROUND_UP(parent_rate, rate);

	if (divider->flags & CLK_DIVIDER_POWER_OF_TWO)
		div = __roundup_pow_of_two(div);
	if (divider->table)
		div = _round_up_table(divider->table, div);

	return div;
}

static int _div_round_closest(struct clk_divider *divider,
		unsigned long parent_rate, unsigned long rate)
{
	int up, down, div;
	unsigned long up_rate, down_rate;

	up = down = div = DIV_ROUND_CLOSEST(parent_rate, rate);

	if (divider->flags & CLK_DIVIDER_POWER_OF_TWO) {
		up = __roundup_pow_of_two(div);
		down = __rounddown_pow_of_two(div);
	} else if (divider->table) {
		up = _round_up_table(divider->table, div);
		down = _round_down_table(divider->table, div);
	}

	up_rate = DIV_ROUND_UP(parent_rate, up);
	down_rate = DIV_ROUND_UP(parent_rate, down);

	return (rate - up_rate) <= (down_rate - rate) ? up : down;
}

static int _div_round(struct clk_divider *divider, unsigned long parent_rate,
		unsigned long rate)
{
	if (divider->flags & CLK_DIVIDER_ROUND_CLOSEST)
		return _div_round_closest(divider, parent_rate, rate);

	return _div_round_up(divider, parent_rate, rate);
}

static bool _is_best_div(struct clk_divider *divider,
		unsigned long rate, unsigned long now, unsigned long best)
{
	if (divider->flags & CLK_DIVIDER_ROUND_CLOSEST)
		return abs(rate - now) < abs(rate - best);

	return now <= rate && now > best;
}

static int _next_div(struct clk_divider *divider, int div)
{
	div++;

	if (divider->flags & CLK_DIVIDER_POWER_OF_TWO)
		return __roundup_pow_of_two(div);
	if (divider->table)
		return _round_up_table(divider->table, div);

	return div;
}

static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate,
		unsigned long *best_parent_rate)
{
	struct clk_divider *divider = to_clk_divider(hw);
	int i, bestdiv = 0;
	unsigned long parent_rate, best = 0, now, maxdiv;
	unsigned long parent_rate_saved = *best_parent_rate;

	if (!rate)
		rate = 1;

	/* if read only, just return current value */
	if (divider->flags & CLK_DIVIDER_READ_ONLY) {
		bestdiv = clk_readl(divider->reg) >> divider->shift;
		bestdiv &= div_mask(divider);
		bestdiv = _get_div(divider, bestdiv);
		return bestdiv;
	}

	maxdiv = _get_maxdiv(divider);

	if (!(__clk_get_flags(hw->clk) & CLK_SET_RATE_PARENT)) {
		parent_rate = *best_parent_rate;
		bestdiv = _div_round(divider, parent_rate, rate);
		bestdiv = bestdiv == 0 ? 1 : bestdiv;
		bestdiv = bestdiv > maxdiv ? maxdiv : bestdiv;
		return bestdiv;
	}

	/*
	 * The maximum divider we can use without overflowing
	 * unsigned long in rate * i below
	 */
	maxdiv = min(ULONG_MAX / rate, maxdiv);

	for (i = 1; i <= maxdiv; i = _next_div(divider, i)) {
		if (!_is_valid_div(divider, i))
			continue;
		if (rate * i == parent_rate_saved) {
			/*
			 * It's the most ideal case if the requested rate can be
			 * divided from parent clock without needing to change
			 * parent rate, so return the divider immediately.
			 */
			*best_parent_rate = parent_rate_saved;
			return i;
		}
		parent_rate = __clk_round_rate(__clk_get_parent(hw->clk),
					       rate * i);
		now = DIV_ROUND_UP(parent_rate, i);
		if (_is_best_div(divider, rate, now, best)) {
			bestdiv = i;
			best = now;
			*best_parent_rate = parent_rate;
		}
	}

	if (!bestdiv) {
		bestdiv = _get_maxdiv(divider);
		*best_parent_rate = __clk_round_rate(__clk_get_parent(hw->clk), 1);
	}

	return bestdiv;
}

static long clk_divider_round_rate(struct clk_hw *hw, unsigned long rate,
				unsigned long *prate)
{
	int div;
	div = clk_divider_bestdiv(hw, rate, prate);

	return DIV_ROUND_UP(*prate, div);
}

static int clk_divider_set_rate(struct clk_hw *hw, unsigned long rate,
				unsigned long parent_rate)
{
	struct clk_divider *divider = to_clk_divider(hw);
	unsigned int div, value;
	unsigned long flags = 0;
	u32 val;

	div = DIV_ROUND_UP(parent_rate, rate);

	if (!_is_valid_div(divider, div))
		return -EINVAL;

	value = _get_val(divider, div);

	if (value > div_mask(divider))
		value = div_mask(divider);

	if (divider->lock)
		spin_lock_irqsave(divider->lock, flags);

	if (divider->flags & CLK_DIVIDER_HIWORD_MASK) {
		val = div_mask(divider) << (divider->shift + 16);
	} else {
		val = clk_readl(divider->reg);
		val &= ~(div_mask(divider) << divider->shift);
	}
	val |= value << divider->shift;
	clk_writel(val, divider->reg);

	if (divider->lock)
		spin_unlock_irqrestore(divider->lock, flags);

	return 0;
}

const struct clk_ops clk_divider_ops = {
	.recalc_rate = clk_divider_recalc_rate,        /* 根據父時鐘計算頻率 */
	.round_rate = clk_divider_round_rate,          /* 計算最接近要求頻率的設定 */
	.set_rate = clk_divider_set_rate,               /* 讀寫暫存器,設定頻率 */     
};

 

其他幾個的和這個類似,就不再這裡說明了。

 

4.時鐘查詢表的註冊

從前面分析,clk本來是以連結串列的形式組成樹的形式的(下節給圖)。但樹的形式對查詢來說就不是很方便了,所以核心中,又對五種形式的時鐘,以clk_lookup_alloc組織的連結串列的形式管理了起來。

下面是管理連結串列的定義

static LIST_HEAD(clocks);
static DEFINE_MUTEX(clocks_mutex);

 

下面看一下是怎麼組織的,後面章節講使用。


/**
 * clk_register_clkdev - register one clock lookup for a struct clk
 * @clk: struct clk to associate with all clk_lookups
 * @con_id: connection ID string on device
 * @dev_id: format string describing device name
 *
 * con_id or dev_id may be NULL as a wildcard, just as in the rest of
 * clkdev.
 *
 * To make things easier for mass registration, we detect error clks
 * from a previous clk_register() call, and return the error code for
 * those.  This is to permit this function to be called immediately
 * after clk_register().
 */
int clk_register_clkdev(struct clk *clk, const char *con_id,
	const char *dev_fmt, ...)
{
	struct clk_lookup *cl;
	va_list ap;

	if (IS_ERR(clk))
		return PTR_ERR(clk);

	va_start(ap, dev_fmt);
	cl = vclkdev_alloc(clk, con_id, dev_fmt, ap);        /* clk繫結到clk_lookup,同時把con_id和引數dev_fmt繫結好 */
	va_end(ap);

	if (!cl)
		return -ENOMEM;

	clkdev_add(cl);            /* 把cl以加入clock連結串列中 */

	return 0;
}


struct clk_lookup_alloc {
	struct clk_lookup cl;
	char	dev_id[MAX_DEV_ID];
	char	con_id[MAX_CON_ID];
};


struct clk_lookup {
	struct list_head	node;
	const char		*dev_id;
	const char		*con_id;
	struct clk		*clk;
};


static inline struct clk_lookup_alloc *__clkdev_alloc(size_t size)
{
	return kzalloc(size, GFP_KERNEL);
}


static struct clk_lookup * __init_refok
vclkdev_alloc(struct clk *clk, const char *con_id, const char *dev_fmt,
	va_list ap)
{
	struct clk_lookup_alloc *cla;

	cla = __clkdev_alloc(sizeof(*cla));    /* 申請clk_lookup_alloc空間 */
	if (!cla)
		return NULL;

	cla->cl.clk = clk;       /* clk繫結到clk_lookup_alloc 裡面的clk_lookup上  */ 
	if (con_id) {            
		strlcpy(cla->con_id, con_id, sizeof(cla->con_id));     /* 把使用者傳的con_id繫結到clk_lookup_alloc的con_id裡 */
		cla->cl.con_id = cla->con_id;        /* 同時clk_lookup 也繫結相同的 */
	}

	if (dev_fmt) {
        /* 同上,繫結dev_id */
		vscnprintf(cla->dev_id, sizeof(cla->dev_id), dev_fmt, ap);
		cla->cl.dev_id = cla->dev_id;
	}

	return &cla->cl;    /* 返回clk_lookup,後面的使用也是用的clk_lookup,不用clk_lookup_alloc  */
}


/* 繫結clk_lookup 到clock連結串列上 */
void clkdev_add(struct clk_lookup *cl)
{
	mutex_lock(&clocks_mutex);
	list_add_tail(&cl->node, &clocks);
	mutex_unlock(&clocks_mutex);
}

 

當然有註冊也必須有刪除的,但是這個是沒有用的,時鐘屬於cpu中的硬體,可以關掉,不能刪除。

/*
 * clkdev_drop - remove a clock dynamically allocated
 */
void clkdev_drop(struct clk_lookup *cl)
{
	mutex_lock(&clocks_mutex);
	list_del(&cl->node);
	mutex_unlock(&clocks_mutex);
	kfree(cl);
}

前面時把時鐘註冊到clock連結串列中。

 

 

下面就是使用了。

先看一下使用方式。

 

現在核心都是和裝置資源管理一塊來使用的,但核心的函式都是一樣的。

struct clk *devm_clk_get(struct device *dev, const char *id)
{
	struct clk **ptr, *clk;

	ptr = devres_alloc(devm_clk_release, sizeof(*ptr), GFP_KERNEL);    /* 申請繫結自動釋放裝置 */
	if (!ptr)
		return ERR_PTR(-ENOMEM);

	clk = clk_get(dev, id);        /* 得到時鐘 */
	if (!IS_ERR(clk)) {
		*ptr = clk;
		devres_add(dev, ptr);
	} else {
		devres_free(ptr);
	}

	return clk;
}

 

 

起始前面第一節已經說過了,這裡再分析一遍。

struct clk *clk_get(struct device *dev, const char *con_id)
{
	const char *dev_id = dev ? dev_name(dev) : NULL;
	struct clk *clk;

	if (dev) {
		clk = of_clk_get_by_name(dev->of_node, con_id);        /* 從裝置樹節點得到clk */
		if (!IS_ERR(clk))                             /* 注意這裡對錯誤判斷取反了,即沒錯的話會直接返回clk */
			return clk;
		if (PTR_ERR(clk) == -EPROBE_DEFER)                 /* 重新獲取 */
			return clk;
	}

	return clk_get_sys(dev_id, con_id);                    /* 裝置樹節點沒找到,從系統註冊連結串列中搜索得到 */
}


struct clk *clk_get_sys(const char *dev_id, const char *con_id)
{
	struct clk_lookup *cl;
 
	mutex_lock(&clocks_mutex);
	cl = clk_find(dev_id, con_id);        /* 根據裝置名或連結名在clock連結串列中查詢到對應的cl*/
	if (cl && !__clk_get(cl->clk))        /* 這裡對找到的時鐘的引用計數+1 */
		cl = NULL;
	mutex_unlock(&clocks_mutex);
 
	return cl ? cl->clk : ERR_PTR(-ENOENT);
}

/*
 * Find the correct struct clk for the device and connection ID.
 * We do slightly fuzzy matching here:
 *  An entry with a NULL ID is assumed to be a wildcard.
 *  If an entry has a device ID, it must match
 *  If an entry has a connection ID, it must match
 * Then we take the most specific entry - with the following
 * order of precedence: dev+con > dev only > con only. 這是重點,即匹配優先順序
 */
static struct clk_lookup *clk_find(const char *dev_id, const char *con_id)
{
	struct clk_lookup *p, *cl = NULL;
	int match, best_found = 0, best_possible = 0;
 
	if (dev_id)        /* 裝置名稱 */
		best_possible += 2;
	if (con_id)        /* 連線名稱(可以是pclk,uart_clk,phy,pci,總是一般都是匯流排的時鐘名稱) */
		best_possible += 1;
 
	list_for_each_entry(p, &clocks, node) {    /* clocks連結串列中查詢,根據dev+con > dev only > con優先順序來匹配 */
		match = 0;
		if (p->dev_id) {
			if (!dev_id || strcmp(p->dev_id, dev_id))
				continue;
			match += 2;
		}
		if (p->con_id) {
			if (!con_id || strcmp(p->con_id, con_id))
				continue;
			match += 1;
		}
 
		if (match > best_found) {
			cl = p;
			if (match != best_possible)
				best_found = match;
			else
				break;
		}
	}
	return cl;
}

可以看到最終就是根據con_id或dev_id來從clock連結串列中搜索匹配,找到最恰當的那個clk。

 

最後說一下,新的4.9.92的核心中,對資料結構做了一些調整。

下面列出不同點3.16.57

/**
 * struct clk_hw - handle for traversing from a struct clk to its corresponding
 * hardware-specific structure.  struct clk_hw should be declared within struct
 * clk_foo and then referenced by the struct clk instance that uses struct
 * clk_foo's clk_ops
 *
 * @clk: pointer to the struct clk instance that points back to this struct
 * clk_hw instance
 *
 * @init: pointer to struct clk_init_data that contains the init data shared
 * with the common clock framework.
 */
struct clk_hw {
	struct clk *clk;
	const struct clk_init_data *init;
};


struct clk {
	const char		*name;
	const struct clk_ops	*ops;
	struct clk_hw		*hw;
	struct module		*owner;
	struct clk		*parent;
	const char		**parent_names;
	struct clk		**parents;
	u8			num_parents;
	u8			new_parent_index;
	unsigned long		rate;
	unsigned long		new_rate;
	struct clk		*new_parent;
	struct clk		*new_child;
	unsigned long		flags;
	unsigned int		enable_count;
	unsigned int		prepare_count;
	unsigned long		accuracy;
	struct hlist_head	children;
	struct hlist_node	child_node;
	unsigned int		notifier_count;
#ifdef CONFIG_DEBUG_FS
	struct dentry		*dentry;
#endif
	struct kref		ref;
};

4.9.92中把

/**
 * struct clk_hw - handle for traversing from a struct clk to its corresponding
 * hardware-specific structure.  struct clk_hw should be declared within struct
 * clk_foo and then referenced by the struct clk instance that uses struct
 * clk_foo's clk_ops
 *
 * @core: pointer to the struct clk_core instance that points back to this
 * struct clk_hw instance
 *
 * @clk: pointer to the per-user struct clk instance that can be used to call
 * into the clk API
 *
 * @init: pointer to struct clk_init_data that contains the init data shared
 * with the common clock framework.
 */
struct clk_hw {
	struct clk_core *core;
	struct clk *clk;
	const struct clk_init_data *init;
};





/***    private data structures    ***/

struct clk_core {
	const char		*name;
	const struct clk_ops	*ops;
	struct clk_hw		*hw;
	struct module		*owner;
	struct clk_core		*parent;
	const char		**parent_names;
	struct clk_core		**parents;
	u8			num_parents;
	u8			new_parent_index;
	unsigned long		rate;
	unsigned long		req_rate;
	unsigned long		new_rate;
	struct clk_core		*new_parent;
	struct clk_core		*new_child;
	unsigned long		flags;
	bool			orphan;
	unsigned int		enable_count;
	unsigned int		prepare_count;
	unsigned long		min_rate;
	unsigned long		max_rate;
	unsigned long		accuracy;
	int			phase;
	struct hlist_head	children;
	struct hlist_node	child_node;
	struct hlist_head	clks;
	unsigned int		notifier_count;
#ifdef CONFIG_DEBUG_FS
	struct dentry		*dentry;
	struct hlist_node	debug_node;
#endif
	struct kref		ref;
};

struct clk {
	struct clk_core	*core;
	const char *dev_id;
	const char *con_id;
	unsigned long min_rate;
	unsigned long max_rate;
	struct hlist_node clks_node;
};

對比可以看到,把之前clk中一些私有的資料,全部都重新分裝了一個結構體。稱作clk_core。

同時也增加來時鐘速率的最大最小分為限制。

同時clk_hw連結來一個clk的所有有效資訊,3.16.57核心還有clk做內部中轉,而4.9.98用clk_hw來作為來一個核心中轉戰。

下面列出4.9.98的核心程式碼

 


/**
 * clk_register_clkdev - register one clock lookup for a struct clk
 * @clk: struct clk to associate with all clk_lookups
 * @con_id: connection ID string on device
 * @dev_id: string describing device name
 *
 * con_id or dev_id may be NULL as a wildcard, just as in the rest of
 * clkdev.
 *
 * To make things easier for mass registration, we detect error clks
 * from a previous clk_register() call, and return the error code for
 * those.  This is to permit this function to be called immediately
 * after clk_register().
 */
int clk_register_clkdev(struct clk *clk, const char *con_id,
	const char *dev_id)
{
	struct clk_lookup *cl;

	if (IS_ERR(clk))
		return PTR_ERR(clk);

	/*
	 * Since dev_id can be NULL, and NULL is handled specially, we must
	 * pass it as either a NULL format string, or with "%s".
	 */
    /* 下面就是差異點,先從clk中拿到clk_hw再使用 */
	if (dev_id)
		cl = __clk_register_clkdev(__clk_get_hw(clk), con_id, "%s",
					   dev_id);
	else
		cl = __clk_register_clkdev(__clk_get_hw(clk), con_id, NULL);

	return cl ? 0 : -ENOMEM;
}


static struct clk_lookup *__clk_register_clkdev(struct clk_hw *hw,
						const char *con_id,
						const char *dev_id, ...)
{
	struct clk_lookup *cl;
	va_list ap;

	va_start(ap, dev_id);
	cl = vclkdev_create(hw, con_id, dev_id, ap);        /* 名字做來調整 */
	va_end(ap);

	return cl;
}

static struct clk_lookup *
vclkdev_create(struct clk_hw *hw, const char *con_id, const char *dev_fmt,
	va_list ap)
{
	struct clk_lookup *cl;

	cl = vclkdev_alloc(hw, con_id, dev_fmt, ap);
	if (cl)
		__clkdev_add(cl);

	return cl;
}


static struct clk_lookup * __ref
vclkdev_alloc(struct clk_hw *hw, const char *con_id, const char *dev_fmt,
	va_list ap)
{
	struct clk_lookup_alloc *cla;

	cla = __clkdev_alloc(sizeof(*cla));
	if (!cla)
		return NULL;

	cla->cl.clk_hw = hw;
	if (con_id) {
		strlcpy(cla->con_id, con_id, sizeof(cla->con_id));
		cla->cl.con_id = cla->con_id;
	}

	if (dev_fmt) {
		vscnprintf(cla->dev_id, sizeof(cla->dev_id), dev_fmt, ap);
		cla->cl.dev_id = cla->dev_id;
	}

	return &cla->cl;
}

 

struct clk *clk_get(struct device *dev, const char *con_id)
{
	const char *dev_id = dev ? dev_name(dev) : NULL;
	struct clk *clk;

	if (dev) {
		clk = __of_clk_get_by_name(dev->of_node, dev_id, con_id);
		if (!IS_ERR(clk) || PTR_ERR(clk) == -EPROBE_DEFER)
			return clk;
	}

	return clk_get_sys(dev_id, con_id);
}


struct clk *clk_get_sys(const char *dev_id, const char *con_id)
{
	struct clk_lookup *cl;
	struct clk *clk = NULL;

	mutex_lock(&clocks_mutex);

	cl = clk_find(dev_id, con_id);
	if (!cl)
		goto out;

	clk = __clk_create_clk(cl->clk_hw, dev_id, con_id);    /* 相比前面增加clk申請來管理 */
	if (IS_ERR(clk))
		goto out;

	if (!__clk_get(clk)) {
		__clk_free_clk(clk);
		cl = NULL;
		goto out;
	}

out:
	mutex_unlock(&clocks_mutex);

	return cl ? clk : ERR_PTR(-ENOENT);
}

/*
每個clock由一個struct clk_core描述,其與struct clk_hw是一一對應的關係,
 但是struct clk可能有很多個,其他驅動需要操作clock時,都需要先分配一個
 struct clk 型別的指標,因此其與struct clk_core是一對多的關係,
 也可以說clk是clk_core的例項 */ 


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 (IS_ERR_OR_NULL(hw))
		return ERR_CAST(hw);

	clk = kzalloc(sizeof(*clk), GFP_KERNEL);
	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_prepare_unlock();

	return clk;
}