从零开始之驱动发开、linux驱动(三十七、linux中common clock framework[2]_provider)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_16777851/article/details/84000223

因为设备树这里还没学习,所以这一节属于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源代码,多模仿,多抄几遍,什么都熟悉了!

猜你喜欢

转载自blog.csdn.net/qq_16777851/article/details/84000223