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