1. 程式人生 > >從零開始之驅動發開、linux驅動(三十七、linux中common clock framework[2]_provider)

從零開始之驅動發開、linux驅動(三十七、linux中common clock framework[2]_provider)

因為裝置樹這裡還沒學習,所以這一節屬於provider章節的完全由蝸窩大神的文章來看。我在暫時只分析一些我知道的,同時對裝置樹這部分的原理和知識也也會盡快學習,補充這篇文章。

http://www.wowotech.net/pm_subsystem/clock_provider.html

 

下面就列出這幾種時鐘的描述

固定速率

比如晶振
門時鐘

和上級時鐘同頻,只能開啟和關閉操作

MUX

多選一(也叫多路複用)

固定倍頻

上級時鐘的頻率有固定倍頻或者分頻,不能關閉

分頻

上級時鐘的頻率分頻,可以選擇不同的分頻比

2. clock有關的DTS

略,檢視上面蝸窩大神的文章。

 

3. clock 相關的資料結構

1)struct clk結構


struct clk {
	const char		*name;                //名字用來在全域性連結串列裡查詢clk用的
	const struct clk_ops	*ops;         //抽象的標準ops操作
	struct clk_hw		*hw;              //clk_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;
};

這個是framework core中關鍵的結構體,核心中都是通過這個結構體來管理clk的,它主要是用來抽象clk硬體的差異,並完成一些通用操作的封裝。

一個系統的clock tree是固定的,因此clock的數目和用途也是固定的。假設上面圖片所描述的是一個系統,它的clock包括osc_clk、pll1_clk、pll2_clk、pll3_clk、hw1_clk、hw2_clk和hw3_clk。我們完全可以通過名字,抽象這7個clock,進行開/關、rate調整等操作。但這樣做有一個缺點:不能很好的處理clock之間的級聯關係,如hw2_clk和hw3_clk都關閉後,pll2_clk才能關閉。因此就引入struct clk結構,以連結串列的形式維護這種關係。

同樣的道理,系統的struct clk,也是固定的,由clock driver在系統啟動時初始化完畢,需要訪問某個clock時,只要獲取它對應的struct clk結構即可。怎麼獲取呢?可以通過名字索引啊!很長一段時間內,kernel及driver就是使用這種方式管理和使用clock的。

最後,裝置(由struct device表示)對應的clock(由struct clk表示)也是固定的啊,可不可以找到裝置就能找到clock?

可以,不過需要藉助device tree。

注:對使用者(device driver)來說,struct clk只是訪問clock的一個控制代碼,不用關心它內部的具體形態。

 

2)struct clk_hw和struct clk_init_data

clock framework使用struct clk結構抽象clock,但該結構對clock consumer是透明的(不需要知道它的內部細節)。同樣,struct clk對clock provider也是透明的。framework提供了struct clk_hw結構,從clock provider的角度,描述clock,該結構的定義如下:


/**
 * struct clk_init_data - holds init data that's common to all clocks and is
 * shared between the clock provider and the common clock framework.
 *
 * @name: clock name
 * @ops: operations this clock supports
 * @parent_names: array of string names for all possible parents
 * @num_parents: number of possible parents
 * @flags: framework-level hints and quirks
 */
struct clk_init_data {
	const char		*name;
	const struct clk_ops	*ops;      //操作函式集,和其它框架的ops作用一樣,提供實際的操作函式。
	const char		**parent_names;    //這是一個字串陣列,儲存了所有可能的parent
	u8			num_parents;           //父時鐘的個數
	unsigned long		flags;           //一些framework級別的flags,後面會詳細說明。
};


/**
 * 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.
 */
//用來連線clk結構體和實際硬體的關係
struct clk_hw {
	struct clk *clk;
	const struct clk_init_data *init;
};

clk_hw結構體可以看到其中封裝了一個clk_ops結構體,它是一個clk驅動需要實現的關鍵結構,廠商需要實現此結構體,並把它註冊到clk framework。clk_hw是聯絡clk_ops和struct clk的紐帶。它一般會被封裝到一個廠商自己定義的更大的結構體中,主要是用來建立與struct clk的聯絡。
 

3)struct clk_ops


/**
 * struct clk_ops -  Callback operations for hardware clocks; these are to
 * be provided by the clock implementation, and will be called by drivers
 * through the clk_* api.
 *
 * @prepare:	Prepare the clock for enabling. This must not return until
 *		the clock is fully prepared, and it's safe to call clk_enable.
 *		This callback is intended to allow clock implementations to
 *		do any initialisation that may sleep. Called with
 *		prepare_lock held.
 *
 * @unprepare:	Release the clock from its prepared state. This will typically
 *		undo any work done in the @prepare callback. Called with
 *		prepare_lock held.
 *
 * @is_prepared: Queries the hardware to determine if the clock is prepared.
 *		This function is allowed to sleep. Optional, if this op is not
 *		set then the prepare count will be used.
 *
 * @unprepare_unused: Unprepare the clock atomically.  Only called from
 *		clk_disable_unused for prepare clocks with special needs.
 *		Called with prepare mutex held. This function may sleep.
 *
 * @enable:	Enable the clock atomically. This must not return until the
 *		clock is generating a valid clock signal, usable by consumer
 *		devices. Called with enable_lock held. This function must not
 *		sleep.
 *
 * @disable:	Disable the clock atomically. Called with enable_lock held.
 *		This function must not sleep.
 *
 * @is_enabled:	Queries the hardware to determine if the clock is enabled.
 *		This function must not sleep. Optional, if this op is not
 *		set then the enable count will be used.
 *
 * @disable_unused: Disable the clock atomically.  Only called from
 *		clk_disable_unused for gate clocks with special needs.
 *		Called with enable_lock held.  This function must not
 *		sleep.
 *
 * @recalc_rate	Recalculate the rate of this clock, by querying hardware. The
 *		parent rate is an input parameter.  It is up to the caller to
 *		ensure that the prepare_mutex is held across this call.
 *		Returns the calculated rate.  Optional, but recommended - if
 *		this op is not set then clock rate will be initialized to 0.
 *
 * @round_rate:	Given a target rate as input, returns the closest rate actually
 *		supported by the clock. The parent rate is an input/output
 *		parameter.
 *
 * @determine_rate: Given a target rate as input, returns the closest rate
 *		actually supported by the clock, and optionally the parent clock
 *		that should be used to provide the clock rate.
 *
 * @set_parent:	Change the input source of this clock; for clocks with multiple
 *		possible parents specify a new parent by passing in the index
 *		as a u8 corresponding to the parent in either the .parent_names
 *		or .parents arrays.  This function in affect translates an
 *		array index into the value programmed into the hardware.
 *		Returns 0 on success, -EERROR otherwise.
 *
 * @get_parent:	Queries the hardware to determine the parent of a clock.  The
 *		return value is a u8 which specifies the index corresponding to
 *		the parent clock.  This index can be applied to either the
 *		.parent_names or .parents arrays.  In short, this function
 *		translates the parent value read from hardware into an array
 *		index.  Currently only called when the clock is initialized by
 *		__clk_init.  This callback is mandatory for clocks with
 *		multiple parents.  It is optional (and unnecessary) for clocks
 *		with 0 or 1 parents.
 *
 * @set_rate:	Change the rate of this clock. The requested rate is specified
 *		by the second argument, which should typically be the return
 *		of .round_rate call.  The third argument gives the parent rate
 *		which is likely helpful for most .set_rate implementation.
 *		Returns 0 on success, -EERROR otherwise.
 *
 * @set_rate_and_parent: Change the rate and the parent of this clock. The
 *		requested rate is specified by the second argument, which
 *		should typically be the return of .round_rate call.  The
 *		third argument gives the parent rate which is likely helpful
 *		for most .set_rate_and_parent implementation. The fourth
 *		argument gives the parent index. This callback is optional (and
 *		unnecessary) for clocks with 0 or 1 parents as well as
 *		for clocks that can tolerate switching the rate and the parent
 *		separately via calls to .set_parent and .set_rate.
 *		Returns 0 on success, -EERROR otherwise.
 *
 * @recalc_accuracy: Recalculate the accuracy of this clock. The clock accuracy
 *		is expressed in ppb (parts per billion). The parent accuracy is
 *		an input parameter.
 *		Returns the calculated accuracy.  Optional - if	this op is not
 *		set then clock accuracy will be initialized to parent accuracy
 *		or 0 (perfect clock) if clock has no parent.
 *
 * @init:	Perform platform-specific initialization magic.
 *		This is not not used by any of the basic clock types.
 *		Please consider other ways of solving initialization problems
 *		before using this callback, as its use is discouraged.
 *
 * @debug_init:	Set up type-specific debugfs entries for this clock.  This
 *		is called once, after the debugfs directory entry for this
 *		clock has been created.  The dentry pointer representing that
 *		directory is provided as an argument.  Called with
 *		prepare_lock held.  Returns 0 on success, -EERROR otherwise.
 *
 *
 * The clk_enable/clk_disable and clk_prepare/clk_unprepare pairs allow
 * implementations to split any work between atomic (enable) and sleepable
 * (prepare) contexts.  If enabling a clock requires code that might sleep,
 * this must be done in clk_prepare.  Clock enable code that will never be
 * called in a sleepable context may be implemented in clk_enable.
 *
 * Typically, drivers will call clk_prepare when a clock may be needed later
 * (eg. when a device is opened), and clk_enable when the clock is actually
 * required (eg. from an interrupt). Note that clk_prepare MUST have been
 * called before clk_enable.
 */
struct clk_ops {
	int		(*prepare)(struct clk_hw *hw);                //開時鐘前呼叫,可能會造成休眠,所以把休眠部分放到這裡,能夠原子操作的放到enable裡
	void		(*unprepare)(struct clk_hw *hw);          //prepare的反操作
	int		(*is_prepared)(struct clk_hw *hw);            //是否prepared
	void		(*unprepare_unused)(struct clk_hw *hw);   //僅僅在clk_disable_unused裡特殊需求呼叫,可能會休眠
	int		(*enable)(struct clk_hw *hw);                 //原子操作,開啟時鐘,這個函式必須在產生實際可用的時鐘訊號後才幹返回
	void		(*disable)(struct clk_hw *hw);            //原子操作,關閉時鐘
	int		(*is_enabled)(struct clk_hw *hw);
	void		(*disable_unused)(struct clk_hw *hw);     //僅僅在clk_disable_unused裡特殊需求呼叫,不能休眠
	unsigned long	(*recalc_rate)(struct clk_hw *hw,
					unsigned long parent_rate);           //查詢硬體,又一次計算頻率
	long		(*round_rate)(struct clk_hw *hw, unsigned long rate,
					unsigned long *parent_rate);          //計算最接近要求的頻率
	long		(*determine_rate)(struct clk_hw *hw, unsigned long rate,
					unsigned long *best_parent_rate,      //返回最接近的頻率
					struct clk **best_parent_clk);
	int		(*set_parent)(struct clk_hw *hw, u8 index);   //MUX會使用(更改此時鐘的輸入源)
	u8		(*get_parent)(struct clk_hw *hw);             //從硬體讀取的父值轉換為陣列下標
	int		(*set_rate)(struct clk_hw *hw, unsigned long rate,
				    unsigned long parent_rate);           //設定頻率
	int		(*set_rate_and_parent)(struct clk_hw *hw,
				    unsigned long rate,                   //更改此時鐘的速率和父級 
				    unsigned long parent_rate, u8 index);
	unsigned long	(*recalc_accuracy)(struct clk_hw *hw,
					   unsigned long parent_accuracy);    //重新計算此時鐘的準確性
	void		(*init)(struct clk_hw *hw);
	int		(*debug_init)(struct clk_hw *hw, struct dentry *dentry);
};

is_prepared,判斷clock是否已經prepared。可以不提供,clock framework core會維護一個prepare的計數(該計數在clk_prepare呼叫時加一,在clk_unprepare時減一),並依據該計數判斷是否prepared;

unprepare_unused,自動unprepare unused clocks;

is_enabled,和is_prepared類似;

disable_unused,自動disable unused clocks;

clock framework core提供一個clk_disable_unused介面,在系統初始化的late_call中呼叫,用於關閉unused clocks,這個介面會呼叫相應clock的.unprepare_unused和.disable_unused函式。

recalc_rate,以parent clock rate為引數,從新計算並返回clock rate;

細心的讀者可能會發現,該結構沒有提供get_rate函式,因為會有一個rate變數快取,另外可以使用recalc_rate。

round_rate,該介面有點特別,在返回rounded rate的同時,會通過一個指標,返回round後parent的rate。這和CLK_SET_RATE_PARENT flag有關,後面會詳細解釋;

init,clock的初始化介面,會在clock被register到核心時呼叫。


/*
 * flags used across common struct clk.  these flags should only affect the
 * top-level framework.  custom flags for dealing with hardware specifics
 * belong in struct clk_foo
 */
#define CLK_SET_RATE_GATE	BIT(0) /* must be gated across rate change */
#define CLK_SET_PARENT_GATE	BIT(1) /* must be gated across re-parent */
#define CLK_SET_RATE_PARENT	BIT(2) /* propagate rate change up one level */
#define CLK_IGNORE_UNUSED	BIT(3) /* do not gate even if unused */
#define CLK_IS_ROOT		BIT(4) /* root clk, has no parent */
#define CLK_IS_BASIC		BIT(5) /* Basic clk, can't do a to_clk_foo() */
#define CLK_GET_RATE_NOCACHE	BIT(6) /* do not use the cached clk rate */
#define CLK_SET_RATE_NO_REPARENT BIT(7) /* don't re-parent on rate change */
#define CLK_GET_ACCURACY_NOCACHE BIT(8) /* do not use the cached clk accuracy */

上面是framework級別的flags,可以使用或的關係,指定多個flags,解釋如下:

CLK_SET_RATE_GATE,表示在改變該clock的rate時,必須gated(關閉);
CLK_SET_PARENT_GATE,表示在改變該clock的parent時,必須gated(關閉);
CLK_SET_RATE_PARENT,表示改變該clock的rate時,要將該改變傳遞到上層parent(下面再詳細說明);
CLK_IGNORE_UNUSED,忽略disable unused的呼叫;
CLK_IS_ROOT,該clock為root clock,沒有parent;
CLK_IS_BASIC,不再使用了;
CLK_GET_RATE_NOCACHE,get rate時,不要從快取中拿,而是從新計算。

round_rate和CLK_SET_RATE_PARENT
當clock consumer呼叫clk_round_rate獲取一個近似的rate時,如果該clock沒有提供.round_rate函式,有兩種方法:
1)在沒有設定CLK_SET_RATE_PARENT標誌時,直接返回該clock的cache rate
2)如果設定了CLK_SET_RATE_PARENT標誌,則會詢問parent,即呼叫clk_round_rate獲取parent clock能提供的、最接近該rate的值。這是什麼意思呢?也就是說,如果parent clock可以得到一個近似的rate值,那麼通過改變parent clock,就能得到所需的clock。
在後續的clk_set_rate介面中,會再次使用該flag,如果置位,則會在設定rate時,傳遞到parent clock,因此parent clock的rate可能會重設。
講的很拗口,我覺得我也沒說清楚,那麼最好的方案就是:在寫clock driver時,最好不用這個flag,簡單的就是最好的(前提是能滿足需求)。

4)struct clk_mux


/**
 * struct clk_mux - multiplexer clock
 *
 * @hw:		handle between common and hardware-specific interfaces
 * @reg:	register controlling multiplexer
 * @shift:	shift to multiplexer bit field
 * @width:	width of mutliplexer bit field
 * @flags:	hardware-specific flags
 * @lock:	register lock
 *
 * Clock with multiple selectable parents.  Implements .get_parent, .set_parent
 * and .recalc_rate
 *
 * Flags:
 * CLK_MUX_INDEX_ONE - register index starts at 1, not 0
 * CLK_MUX_INDEX_BIT - register index is a single bit (power of two)
 * CLK_MUX_HIWORD_MASK - The mux settings are only in lower 16-bit of this
 *	register, and mask of mux bits are in higher 16-bit of this register.
 *	While setting the mux bits, higher 16-bit should also be updated to
 *	indicate changing mux bits.
 */
//多路時鐘源
struct clk_mux {
	struct clk_hw	hw;        //指向硬體時鐘結構體
	void __iomem	*reg;      //對應SOC的暫存器
	u32		*table;
	u32		mask;              //當前時鐘遮蔽字,即在reg暫存器中的位寬
	u8		shift;             //當前時鐘在reg暫存器的偏移位置
	u8		flags;             //見上面註釋
	spinlock_t	*lock;         
};

5)struct clk_fixed_rate (固定頻率的時鐘結構)


/*
 * DOC: Basic clock implementations common to many platforms
 *
 * Each basic clock hardware type is comprised of a structure describing the
 * clock hardware, implementations of the relevant callbacks in struct clk_ops,
 * unique flags for that hardware type, a registration function and an
 * alternative macro for static initialization
 */

/**
 * 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;        //固定範圍的時鐘,如12M、32.768K
	unsigned long	fixed_accuracy;
	u8		flags;
};

6)struct clk_gate(使能時鐘結構體)


/**
 * struct clk_gate - gating clock
 *
 * @hw:		handle between common and hardware-specific interfaces
 * @reg:	register controlling gate
 * @bit_idx:	single bit controlling gate
 * @flags:	hardware-specific flags
 * @lock:	register lock
 *
 * Clock which can gate its output.  Implements .enable & .disable
 *
 * Flags:
 * CLK_GATE_SET_TO_DISABLE - by default this clock sets the bit at bit_idx to
 *	enable the clock.  Setting this flag does the opposite: setting the bit
 *	disable the clock and clearing it enables the clock
 * CLK_GATE_HIWORD_MASK - The gate settings are only in lower 16-bit
 *	of this register, and mask of gate bits are in higher 16-bit of this
 *	register.  While setting the gate bits, higher 16-bit should also be
 *	updated to indicate changing gate bits.
 */
struct clk_gate {
	struct clk_hw hw;            //指向硬體時鐘
	void __iomem	*reg;        //使能暫存器
	u8		bit_idx;             //對應reg暫存器中的使能bit位
	u8		flags;
	spinlock_t	*lock;
};

7)struct clk_lookup(時鐘檢索)

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

8)struct clk_fixed_factor(固定乘法器PLL和時鐘分頻DIV)


/**
 * struct clk_fixed_factor - fixed multiplier and divider clock
 *
 * @hw:		handle between common and hardware-specific interfaces
 * @mult:	multiplier
 * @div:	divider
 *
 * Clock with a fixed multiplier and divider. The output frequency is the
 * parent clock rate divided by div and multiplied by mult.
 * Implements .recalc_rate, .set_rate and .round_rate
 */

struct clk_fixed_factor {
	struct clk_hw	hw;
	unsigned int	mult;
	unsigned int	div;
};

 

4 clock tree建立相關的API

4.1 clk_register

系統中,每一個clock都有一個struct clk_hw變數描述,clock provider需要使用register相關的介面,將這些clock註冊到kernel,clock framework的核心程式碼會把它們轉換為struct clk變數,並以tree的形式組織起來。這些介面的原型如下:

struct clk *clk_register(struct device *dev, struct clk_hw *hw);
struct clk *devm_clk_register(struct device *dev, struct clk_hw *hw);

void clk_unregister(struct clk *clk);
void devm_clk_unregister(struct device *dev, struct clk *clk);

這些API比較簡單(複雜的是怎麼填充struct clk_hw變數),register介面接受一個填充好的struct clk_hw指標,將它轉換為sruct clk結構,並根據parent的名字,新增到clock tree中。

 

4.2 clock分類及register

根據clock的特點,clock framework將clock分為fixed rate、gate、devider、mux、fixed factor、composite六類,每一類clock都有相似的功能、相似的控制方式,因而可以使用相同的邏輯,統一處理,這充分體現了面向物件的思想。

1)fixed rate clock

這一類clock具有固定的頻率,不能開關、不能調整頻率、不能選擇parent、不需要提供任何的clk_ops回撥函式,是最簡單的一類clock。

可以直接通過DTS配置的方式支援,clock framework core能直接從DTS中解出clock資訊,並自動註冊到kernel,不需要任何driver支援。

clock framework使用struct clk_fixed_rate結構抽象這一類clock,另外提供了一個介面,可以直接註冊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;
};

extern const struct clk_ops clk_fixed_rate_ops;
struct clk *clk_register_fixed_rate(struct device *dev, const char *name,
		const char *parent_name, unsigned long flags,
		unsigned long fixed_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);

void of_fixed_clk_setup(struct device_node *np);

clock provider一般不需要直接使用struct clk_fixed_rate結構,因為clk_register_fixed_rate介面是非常方便的;

clk_register_fixed_rate介面以clock name、parent name、fixed_rate為引數,建立一個具有固定頻率的clock,該clock的clk_ops也是clock framework提供的,不需要provider關心;

clk_register_fixed_rate_with_accuracy介面是使用時鐘框架註冊固定速率時鐘;

of_fixed_clk_setup簡易固定速率時鐘的設定功能;

如果使用DTS的話,clk_register_fixed_rate都不需要,直接在DTS中配置即可;

上面兩個其實是一樣的

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

2)gate clock

這一類clock只可開關(會提供.enable/.disable回撥),可使用下面介面註冊:

struct clk *clk_register_gate(struct device *dev, const char *name,
		const char *parent_name, unsigned long flags,
		void __iomem *reg, u8 bit_idx,
		u8 clk_gate_flags, spinlock_t *lock);

需要提供的引數包括:

name,clock的名稱;

parent_name,parent clock的名稱,沒有的話可留空;

flags,可參考3.1中的說明;

reg,控制該clock開關的暫存器地址(虛擬地址);

bit_idx,控制clock開關的bit位(是1開,還是0開,可通過下面gate特有的flag指定);

clk_gate_flags,gate clock特有的flag,當前只有一種:CLK_GATE_SET_TO_DISABLE,clock開關控制的方式,如果置位,表示寫1關閉clock,反之亦然;

lock,如果clock開關時需要互斥,可提供一個spinlock。

3)divider clock

這一類clock可以設定分頻值(因而會提供.recalc_rate/.set_rate/.round_rate回撥),可通過下面兩個介面註冊:

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

該介面用於註冊分頻比規則的clock:

reg,控制clock分頻比的暫存器;

shift,控制分頻比的bit在暫存器中的偏移;

width,控制分頻比的bit位數,預設情況下,實際的divider值是暫存器值加1。如果有其它例外,可使用下面的的flag指示;

clk_divider_flags,divider clock特有的flag,包括:

        CLK_DIVIDER_ONE_BASED,實際的divider值就是暫存器值(0是無效的,除非設定CLK_DIVIDER_ALLOW_ZERO flag);
        CLK_DIVIDER_POWER_OF_TWO,實際的divider值是暫存器值得2次方;
        CLK_DIVIDER_ALLOW_ZERO,divider值可以為0(不改變,視硬體支援而定)。

如有需要其他分頻方式,就需要使用另外一個介面,如下:

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

該介面用於註冊分頻比不規則的clock,和上面介面比較,差別在於divider值和暫存器值得對應關係由一個table決定,該table的原型為:

struct clk_div_table {
        unsigned int    val;
        unsigned int    div;
};

其中val表示暫存器值,div表示分頻值,它們的關係也可以通過clk_divider_flags改變。

4)mux clock

這一類clock可以選擇多個parent,因為會實現.get_parent/.set_parent/.recalc_rate回撥,可通過下面兩個介面註冊:

struct clk *clk_register_mux(struct device *dev, const char *name,
		const char **parent_names, u8 num_parents, unsigned long flags,
		void __iomem *reg, u8 shift, u8 width,
		u8 clk_mux_flags, spinlock_t *lock);

該介面可註冊mux控制比較規則的clock(類似divider clock):

parent_names,一個字串陣列,用於描述所有可能的parent clock;

num_parents,parent clock的個數;

reg、shift、width,選擇parent的暫存器、偏移、寬度,預設情況下,暫存器值為0時,對應第一個parent,依此類推。如有例外,可通過下面的flags,以及另外一個介面實現;

clk_mux_flags,mux clock特有的flag:

        CLK_MUX_INDEX_ONE,暫存器值不是從0開始,而是從1開始;
        CLK_MUX_INDEX_BIT,暫存器值為2的冪。

struct clk *clk_register_mux_table(struct device *dev, const char *name,
		const char **parent_names, u8 num_parents, unsigned long flags,
		void __iomem *reg, u8 shift, u32 mask,
		u8 clk_mux_flags, u32 *table, spinlock_t *lock);

該介面通過一個table,註冊mux控制不規則的clock,原理和divider clock類似,不再詳細介紹。

5)fixed factor clock

這一類clock具有固定的factor(即multiplier和divider),clock的頻率是由parent clock的頻率,乘以mul,除以div,多用於一些具有固定分頻係數的clock。由於parent clock的頻率可以改變,因而fix factor clock也可該改變頻率,因此也會提供.recalc_rate/.set_rate/.round_rate等回撥。

可通過下面介面註冊:

struct clk *clk_register_fixed_factor(struct device *dev, const char *name,
		const char *parent_name, unsigned long flags,
		unsigned int mult, unsigned int div);

另外,這一類介面和fixed rateclock類似,不需要提供driver,只需要配置dts即可。

void __init of_fixed_factor_clk_setup(struct device_node *node);

6)composite clock

顧名思義,就是mux、divider、gate等clock的組合,可通過下面介面註冊:

struct clk *clk_register_composite(struct device *dev, const char *name,
			const char **parent_names, int num_parents,
			struct clk_hw *mux_hw, const struct clk_ops *mux_ops,
			struct clk_hw *rate_hw, const struct clk_ops *rate_ops,
			struct clk_hw *gate_hw, const struct clk_ops *gate_ops,
			unsigned long flags);

看著有點複雜,但理解了上面1~5類clock,這裡就只剩下苦力了,耐心一點,就可以了。

 

4.3 DTS相關的API

再回到第2章DTS相關的介紹,clock driver使用一個DTS node描述一個clock provider,而clock consumer則會使用類似“clocks = <&clock  32>, <&clock 45>;”的形式引用,clock framework會自行把這些抽象的數字轉換成實際的struct clk結構,怎麼做的呢?肯定離不開clock provider的幫助。

上面描述的regitser介面,負責把clocks抽象為一個一個的struct clock,與此同時,clock provider需要把這些struct clk結構儲存起來,並呼叫clock framework的介面,將這些對應資訊告知framework的OF模組,這樣才可以幫助將clock consumer的DTS描述轉換為struct clk結構。該介面如下:

int of_clk_add_provider(struct device_node *np,
			struct clk *(*clk_src_get)(struct of_phandle_args *clkspec,
						   void *data),
			void *data);

np,device_node指標,clock provider在和自己的DTS匹配時獲得;

clk_src_get,獲取struct clk指標的回撥函式,由clock provider根據實際的邏輯實現,引數說明如下:

        args,struct of_phandle_args型別的指標,由DTS在解析引數時傳遞。例如上面的“clocks = <&clock  32>, <&clock 45>;”,32、45就是通過這個指標傳進來的;

        data,儲存struct clk結構的指標,通常是一個數組,具體由provider決定。

data,和回撥函式中的data意義相同,只是這裡由provider提供,get時由clock framework core傳遞給回撥函式。

 

5. 使用clock framework編寫clock驅動的步驟

編寫clock driver的步驟大概如下:

1)分析硬體的clock tree,按照上面所描述的分類,講這些clock分類。

2)將clock tree在DTS中描述出來,需要注意以下幾2點:

        a)對於fixed rate clocks,.compatible固定填充"fixed-clock",並提供"clock-frequency"和"clock-output-names"關鍵字。之後不需要再driver中做任何處理,clock framework core會幫我們搞定一切。

        b)同樣,對於fixed factor clock,.compatible為"fixed-factor-clock",並提供"clock-div"、"clock-mult"和"clock-output-names"關鍵字。clock framework core會幫我們搞定一切。

        切記,儘量利用kernel已有資源,不要多寫一行程式碼,簡潔的就是美的!

3)對於不能由clock framework core處理的clock,需要在driver中使用struct of_device_id進行匹配,並在初始化時,呼叫OF模組,查詢所有的DTS匹配項,並執行合適的regitser介面,註冊clock。

4)註冊clock的同時,將返回的struct clk指標,儲存在一個數組中,並呼叫of_clk_add_provider介面,告知clock framework core。

5)最後,也是最重要的一點,多看kernel原始碼,多模仿,多抄幾遍,什麼都熟悉了!