4.2.5. 设计说明
4.2.5.1. 源码说明
内核的时钟驱动框架位于linux-5.10/drivers/clk目录下,CMU的底层驱动位于/drivers/clk/zx/目录下。
ZX的目录结构如下图所示:
文件 |
说明 |
---|---|
clk-aic.h |
aic公用头文件 |
clk-aic.c |
CMU各个时钟的初始化,注册文件 |
clk-disp.c |
显示模块的时钟文件 |
clk-fixed-parent-mod.c |
只有一个父时钟源的时钟文件 |
clk-multi-parent-mod.c |
具有多个父时钟源的时钟文件 |
clk-pll.c |
PLL时钟文件 |
4.2.5.2. 模块架构
4.2.5.2.1. clock
按照CCF框架,时钟分为六类:
fixed rate clock
gate clock
divider clock
mux clock
fixed clock
composite clock
时钟树中的每一个divider、gate、mux等都需要定义一个struct clk_hw结构体。CMU模块中有非常多的gate和divider,所以为了代码的简洁性和易用性,CMU的驱动并未严格按照CCF框架编写。CMU驱动模块将时钟分为五种类型:
fixed rate clock
fixed parent module clock
multiple parent module clock
display module clock
pll clock
fixed rate clock包含OSC24M、RC1M、OSC32K三个时钟,这种时钟频率固定,不能调节频率,不能打开或关闭(即底层ops无enable和disable函数)。
fixed parent module clock实现只有一个父时钟源的时钟驱动,主要是各个外设模块的时钟,该类型时钟可以改变时钟频率,打开或关闭时钟,获取父时钟源参数,但不能设置或改变父时钟源。
multiple parent module clock实现有多个父时钟源的时钟驱动,主要是各种总线时钟,该类型的时钟最为复杂,可以打开或关闭时钟,调节频率,获取或改变父时钟源。
display module clock实现了几个与显示模块相关的时钟驱动,由于显示模块除了自身的模块时钟外,还有一个像素时钟,相应的底层寄存器的设计也不同,所以将显示相关的几个时钟重新设计了底层驱动。
pll clock实现了CMU的pll时钟驱动。
在上述的几种分类中,每中分类都自定义了一个该类型的结构体,基于该结构体实现各种时钟操作。在fixed parent module的结构体中,定义了模块的bus_gate和module_gate,以及该类型时钟的分频系数,相当于综合了CCF框架中的gate和divider。multiple parent module的结构体中定义了gate,mux以及分频系数,相当于综合了CCF框架中的gate,divider和mux。几种类型的时钟支持的API接口如下:
类型 |
fixed rate clock |
fixed parent clock |
multi parent clock |
disp clock |
pll clock |
---|---|---|---|---|---|
clk_prepare |
√ |
√ |
√ |
√ |
|
clk_prepare_enable |
|||||
clk_unprepare |
√ |
√ |
√ |
√ |
|
clk_disable_unprepare |
|||||
clk_set_rate |
√ |
√ |
√ |
√ |
|
clk_get_rate |
√ |
√ |
√ |
√ |
√ |
clk_round_rate |
√ |
√ |
√ |
√ |
|
clk_set_parent |
√ |
||||
clk_get_parent |
√ |
||||
recalc_rate |
√ |
√ |
√ |
√ |
√ |
4.2.5.2.1.1. 时钟树框图
根据CMU驱动中对时钟的五种分类,对时钟树中各个时钟的归类进行了划分,如上图所示。
4.2.5.2.1.2. fixed rate clock
属于该类型的时钟有:
类型 |
时钟 |
---|---|
fixed rate clock |
OSC24M |
OSC32K |
|
RC1M |
4.2.5.2.1.3. fixed parent clock
类型 |
时钟 |
---|---|
fixed parent clock |
CLK_DMA |
CLK_CE |
|
CLK_USBD |
|
CLK_USBH0-1 |
|
CLK_USB_PHY0-1 |
|
CLK_GMAC0-1 |
|
CLK_SPI0-1 |
|
CLK_SDMMC0-2 |
|
CLK_SYSCON |
|
CLK_RTC |
|
CLK_I2S0-1 |
|
CLK_ADDA |
|
CLK_DE |
|
CLK_GE |
|
CLK_VE |
|
CLK_WDOG |
|
CLK_SID |
|
CLK_GTC |
|
CLK_GPIO |
|
CLK_UART0-7 |
|
CLK_TWI0-3 |
|
CLK_CAN0-1 |
|
CLK_PWM |
|
CLK_ADCIM |
|
CLK_GPADC |
|
CLK_RTP |
|
CLK_TSEN |
|
CLK_CIR |
|
CLK_RGB |
|
CLK_LVDS |
|
CLK_MIPIDSI |
4.2.5.2.1.4. multiple parent clock
属于该类型的时钟有:
类型 |
时钟 |
---|---|
multi parent clock |
CLK_CPU |
CLK_AHB0 |
|
CLK_APB0 |
|
CLK_APB1 |
|
CLK_AXI0 |
|
CLK_OUT0 |
|
CLK_OUT1 |
|
CLK_OUT2 |
|
CLK_OUT3 |
4.2.5.2.1.5. pll clock
属于该类型的时钟有:
类型 |
时钟 |
---|---|
pll clock |
CLK_PLL_INT0 |
CLK_PLL_INT1 |
|
CLK_PLL_FRA0 |
|
CLK_PLL_FRA1 |
|
CLK_PLL_FRA2 |
4.2.5.2.1.6. disp clock
属于该类型的时钟有:
类型 |
时钟 |
---|---|
disp clock |
CLK_PIX |
CLK_SCLK |
4.2.5.2.2. reset
CMU模块的reset驱动实现基于内核提供的reset framework。其实现过程是创建并填充内核提供的reset controller设备结构体(struct reset_controller_dev),并调用相应的接口:
reset_controller_register
reset_controller_unregister
注册或注销。reset controller的结构体如下:
struct reset_controller_dev {
const struct reset_control_ops *ops;
struct module *owner;
struct list_head list;
struct list_head reset_control_head;
struct device *dev;
struct device_node *of_node;
int of_reset_n_cells;
int (*of_xlate)(struct reset_controller_dev *rcdev,
const struct of_phandle_args *reset_spec);
unsigned int nr_resets;
};
驱动实现过程主要是对reset_control_ops结构体中的函数指针进行填充,基本上是reset驱动的所有工作量。在CMU模块的reset驱动中,实现了对assert和deassert及status三个函数指针的填充。
4.2.5.3. 关键流程设计
4.2.5.3.1. 初始化流程
4.2.5.3.1.1. clock驱动初始化
通过CLK_OF_DECLARE宏定义,CMU的clock驱动会在__clock_of_table段存放一个struct of_device_id类型的变量。在系统初始化内核时,调用of_clk_init函数,在该函数中调用相应的时钟初始化函数。初始化流程如下:
4.2.5.3.1.2. reset驱动初始化
通过postcore_initcall宏,将reset驱动存放到.initcall2.init段中。在系统初始化内核时,调用aic_reset_init函数进行reset controller的初始化和注册。
4.2.5.4. 数据结构设计
CMU模块关键结构体定义如下:
4.2.5.4.1. fixed_parent_clk_cfg
struct fixed_parent_clk_cfg { //fixed parent clock的配置结构体
u32 id; //fixed parent clock的索引值,参考3.2节CLK_xxx
u16 type;
u8 fact_mult;
u8 fact_div;
const char *name; //fixed parent clock的名字
const char * const *parent_names; //父时钟的名字
int num_parents; //父时钟个数
u32 offset_reg; //时钟在CMU中的偏移地址
s8 bus_gate_bit; //总线使能位偏移
s8 mod_gate_bit; //模块使能位偏移
u8 div_bit; //分频系数偏移
u8 div_width; //分频系数所占位宽
struct clk_hw *(*func)(void __iomem *base, const struct fixed_parent_clk_cfg *cfg); //指向初始化和注册fixed parent时钟的函数指针
};
4.2.5.4.2. multi_parent_clk_cfg
struct multi_parent_clk_cfg { //multi parent clock的配置结构体
u32 id; //multi parent clock的索引值,参考3.3节CLK_xxx
const char *name;
const char * const *parent_names;
int num_parents;
u32 offset_reg;
s32 gate_bit;
u8 mux_bit; //父时钟源选择位的bit偏移
u8 mux_width; //父时钟源选择位所占位宽
u8 div0_bit; //分频系数偏移
u8 div0_width; //分频系数所占位宽
struct clk_hw *(*func)(void __iomem *base, const struct multi_parent_clk_cfg *cfg); //指向初始化和注册multi parent时钟的函数指针
};
4.2.5.4.3. pll_clk_cfg
struct pll_clk_cfg { //pll时钟的配置结构体
u32 id; //pll时钟的索引值,参考3.4节CLK_xxx
enum aic_pll_type type; //pll时钟的类型,是整数分频还是小数分频
const char *name;
const char * const *parent_names;
int num_parents;
u32 offset_int; //整数分频寄存器的偏移
u32 offset_fra; //小数分频寄存器的偏移
u32 offset_sdm; //展频寄存器的偏移
struct clk_hw *(*func)(void __iomem *base, const struct pll_clk_cfg *cfg); //指向初始化和注册pll时钟的函数指针
};
4.2.5.4.4. disp_clk_cfg
struct disp_clk_cfg { //显示模块时钟配置的结构体
u32 id; //显示模块时钟的索引值,参考3.5节CLK_xxx
const char *name;
const char * const *parent_names;
int num_parents;
u32 offset_reg; //显示模块时钟使能寄存器
s8 bus_gate_bit; //显示模块总线使能位偏移
s8 mod_gate_bit; //显示模块模块使能位偏移
u32 offset_div_reg; //显示模块分频寄存器偏移
u8 divn_bit; //分频系数N偏移
u8 divn_width; //分频系数N所占位宽
u8 divm_bit; //分频系数M偏移
u8 divm_width; //分频系数M所占位宽
u8 flag_bit; //分频系数M标志位
struct clk_hw *(*func)(void __iomem *base, const struct disp_clk_cfg *cfg); //指向初始化和注册显示模块时钟的函数指针
};
4.2.5.5. 接口设计
4.2.5.5.1. aic_clk_hw_fixed_parent_module
函数原型 |
struct clk_hw *aic_clk_hw_fixed_parent(void __iomem *base, const struct fixed_parent_clk_cfg *cfg) |
---|---|
功能说明 |
初始化fixed parent clock,并对时钟进行注册 |
参数定义 |
base:CMU寄存器的基地址
cfg:指向配置参数的指针
|
返回值 |
返回struct clk_hw*类型的指针 |
注意事项 |
4.2.5.5.2. aic_clk_hw_multi_parent_module
函数原型 |
struct clk_hw *aic_clk_hw_multi_parent(void __iomem *base, const struct multi_parent_clk_cfg *cfg) |
---|---|
功能说明 |
初始化multi parent clock,并对时钟进行注册 |
参数定义 |
base:CMU寄存器的基地址
cfg:指向配置参数的指针
|
返回值 |
返回struct clk_hw*类型的指针 |
注意事项 |
4.2.5.5.3. aic_clk_hw_pll
函数原型 |
struct clk_hw *aic_clk_hw_pll(void __iomem *base, const struct pll_clk_cfg *cfg) |
---|---|
功能说明 |
初始化pll clock,并对时钟进行注册 |
参数定义 |
base:CMU寄存器的基地址
cfg:指向配置参数的指针
|
返回值 |
返回struct clk_hw*类型的指针 |
注意事项 |
4.2.5.5.4. aic_clk_hw_disp
函数原型 |
struct clk_hw *aic_clk_hw_disp(void __iomem *base, const struct disp_clk_cfg *cfg) |
---|---|
功能说明 |
初始化disp clock,并对时钟进行注册 |
参数定义 |
base:CMU寄存器的基地址
cfg:指向配置参数的指针
|
返回值 |
返回struct clk_hw*类型的指针 |
注意事项 |