linux设备驱动程序之时钟管理
时钟管理模块是linux系统为统一管理各硬件的时钟而实现管理框架,负责所有模块的时钟调节和电源管理。时钟管理模块主要负责处理各硬件模块的工作频率调节及电源切换管理。一个硬件模块要正常工作,必须先配置好硬件的工作频率、打开电源开关、总线访问开关等操作,时钟管理模块为设备驱动提供统一的操作接口,使驱动不用关心时钟硬件实现的具体细节
1.系统时钟结构
系统时钟主要是指一些源时钟,为其它硬件模块提供时钟源输入。系统时钟一般为多个硬件模块共享,不允许随意调节。
系统上一般只有两个时源头:低频晶振(LOSC)32Khz和高频晶振(HOSC)24Mhz,系统在HOSC的基础上,增加一些锁相环电路,实现更高的时钟频率输出。为了便于控制一些模块的时钟频率,系统对时钟源进行了分组,实现较多的锁相环电路,以实现分路独立调节。
由于CPU、总线的时钟比较特殊,其工作时钟也经常会输出作为某些其它模块的时钟源,因此,我们也将此类时钟归结为系统时钟。其结构图如下:
各PLL的分工如下:
pll_cpu只作为CPU的时钟源,不作他用;
pll_audio只作为音频模块(如codec、iis、spdif等)的时钟源,不作他用;
pll_video0、pll_video1一般作为显示相关模块(如de、csi、hdmi等)的时钟源;
pll_ve一般只作为视频解码模块(ve)的时钟源;
pll_ddr0、pll_ddr1一般只作为DDR的时钟源;
hosc用作一些外设接口模块(如nand、sdmmc、usb等)的时钟源;
pll_gpu一般只作为GPU模块的时钟源;
pll_periph0、pll_periph1是两个通用时钟源,可以为多个模块共享
2.模块时钟结构
模块时钟主要是针对一些具体模块(如:gpu、de),在时钟频率配置、电源控制、访问控制等方面进行管理。一个典型的模块如下图所示,包含module gateing、ahb gating、dram gating,以及reset控制。要想一个模块能够正常工作,必须在这几个方面作好相关的配置。
硬件设计时,为每个硬件模块定义好了可选的时钟源(有些默认使用总线的工作时钟作时钟源),时钟源的定义如上节所述,模块只能在相关可能的时钟源间作选择。
模块的电源管理体现在两个方面:模块的时钟使能和模块控制器复位,相关驱动需要通过以下所列的时钟进行控制。
CCU的源码结构如下图所示:
|---drivers
||---clk
|||---xxx
||||---clk-xxx.h
||||---clk-periph.h
||||---clk-sunxi.h
||||---clk-factors.h
||||---clk-factors.c
||||---clk-periph.c
||||---clk-xxx.c
||||---clk-default.c
||||---clk-xxx_tbl.c
||||---clk-debugfs.c
|---include
||---linux
|||---clk.h
|||---clk
|
clk.h 时钟子系统提供的API接口定义;
clk-factors.c: 针对PLLx等系统时钟(以HOSC为source)的公用代码
clk-periph.c:针对各模块时钟的公用代码
clk-xxx.c:针对xxx的实现
clk-default.c: 第二级初始化操作
clk-debugfs.c: 针对debugfs的调试接口
clk-xxx_tbl.c:针对xxx平台clk table
3.使用方法
(1) 一定要判断clk_get返回句柄的有效性.比如:
规范写法:
struct clk *pll = clk_get(NULL, "sys_pll3"); if(!pll || IS_ERR(pll)){ /* 获取时钟句柄失败 */ printk(“try to get pll3 failed!\n”); } ... |
|
(2) 有返回值的clk api一定要判断返回值,返回失败时建议加打印,比如:
规范写法:
if(clk_enable(pll)) { /* 使能 PLL3输出失败 */ printk("try to enable pll3 output failed!\n"); } |
(3) clk_disable或clk_put之前,先检查句柄的有效性;clk_put之后将句柄清空.比如:
if(NULL == sdc0_clk || IS_ERR(sdc0_clk)){ printk("sdc0_clk handle is invalid, just return!\n"); return; } else { clk_disable(sdc0_clk); clk_put(sdc0_clk); sdc0_clk = NULL; } |
(4)设置PLL3频率
struct clk *pll = clk_get(NULL, "sys_pll3"); if(!pll || IS_ERR(pll)){ /* 获取时钟句柄失败 */ printk(“try to get pll3 failed!\n”); } /* 使能时钟 */ if(clk_prepare_enable(pll)) { /* 使能 PLL3输出失败 */ printk("try to enable pll3 output failed!\n"); } /* 设置 PLL3的频率为270Mhz */ if(clk_set_rate(pll, 270000000)) { /* 设置 PLL3频率失败 */ printk("try to set rate to 270000000 failed!\n"); } |
更多推荐
所有评论(0)