linux clk模型

来源:互联网 发布:淘宝买家等级 编辑:程序博客网 时间:2024/05/20 13:14
本模块是对平台(CPU 内部集成)的所有时钟源进行建模,提供对时钟源(clk)的操作接口。

Clocking rate (Crystal/DPLL/ARM core): 26.0/332/500 MHz 

clk,clocksource, 锁相环(phase lock loops: pll.),多个时钟源,晶振,脉冲频率,时钟频率,晶振漂移,
clk表示CPU内部的一个频率(输入或输出)
clocksource,表示一个硬时钟。(RTC,PIT, TSC?)
CPU内部会有DPLLs,这些DPLLS用于产生许多不现的频率,供外设,总线等使用。
对DPLLS的控制都是通过寄存器,其中有XX_CLKSEL_xx,这个寄存器控制这个DPLLS输出的进行分频的除数。DIV值。:dpll m*n/p
在omap34xxCPU中,基本所有的频率都是由sys_clk,经dplls锁相后,向外输出得到的。
而sys_clk其实就是 内部自己产生的输入:osc_sys_clk 或者是 外部晶振输入。



1: //节电,给多个设备重用时钟。
一般情况下,固件启动的过程会输出一个描述板子上所有存在设备的表。如果没有这个表,系统启动代码建立正确的设备的唯一方法就是为一个特定的板子编译一个kernel。这种board-specific kernel广泛用在嵌入式和一般系统的开发上。
//即,通用的启动过程时,由固件引导初始化硬件,并把一个描述板子上所有设备的表传给系统。然后系统就可以进行对应的加载,实现PC机的通用化,启动。
//但对于嵌入式来说,一般是根据特定的板,生成特定的内核。

在大部分情况下,设备的memory和IRQ资源不足够让驱动正常工作。board setup code会用device的platform_data域来为设备提供一些额外的资源。/*嵌入式系统上的设备会频繁地使用一个或者多个时钟,这些时钟因为节电的原因只有在真正使用的时候才会被打开,系统在启动过程中会为设备分配时钟*/,可以通过clk_get(&pdev->dev, clock_name)来获得需要的时钟。


/* 
 clk初始化,采用一个链表把所有到时钟结构体链接在一起,该链表的头是clocks。 
 davinci_clk_init()例程由io.c中的davinci_map_common_io()例程调用,后者被
 davinci_map_io()例程调用,而其又被注册到board_evm.c中的机器描述符中(struct machine_desc),
 故具体调用过程是:
 start_kernel()-->setup_arch()-->paging_init()-->  mdesc->map_io()(其就是davinci_map_io())-->davinci_map_common_io()-->davinci_clk_init()。
*/
2:
arch/arm/plat_xx: 特定平台的通用共用的代码。 平台
arch/arm/mach_xx: 完全针对单个板的底层代码。 单个板

3:
从一个时钟产生器(CLOCK generator)中产生的时钟分两类:
interfaces clocks: 供给接口电路(总线?) xx_ICLK
functional clocks: 供给功能模块。xx_FCLK

clk结构是根据平台自定的结构。
clk模型是平台特定的,
/arch/arm/xx平台/clock.h: 提供clk结构,及操作接口,宏定义。
/arch/arm/xx平台/clock.c: 提供操作于该平台的时钟的操作。


omap3430:
static LIST_HEAD(clocks);//全局的所有clocks的链表。
static struct clk_functions *arch_clock;//系统的 平台结构相关时钟操作结构.
static LIST_HEAD(clk_notifier_list);//全局变量:所有的时钟通知结构 链表, 一个时钟可能对应一串的通知结构.notifier_head.
//时钟回调结构, 用于监控某个CLK,当CLK变化时则会通知
struct clk_notifier {
struct clk*clk;
struct blocking_notifier_headnotifier_head;
struct list_headnode;
};

__setup("mpurate=", omap_clk_setup);//?????????????????

/* Propagate rate to children */
void propagate_rate(struct clk *tclk, u8 rate_storage)


//******************clk 实现 *************************
linux 中clk 模型,由于clk是与具体平台相关的部分。所以这部分是平台自定。
而要omap中,它把这个clk模型,拆分成两部分:公共代码部分,平台板相关部分。
(arch/arm/plat_xx: 为系列平台相关的公共代码实现,会调用私有部分的接口实现)
(arch/arm/mach_xx: 为特定单个平台板相关的完成私有代码实现,会调用公共部分的接口实现)
最终向内核关联,初始化的是由 单个平台板上的代码来做。arch_initcall

//*******************omap 上的实现*********************************
omap34xx上有许多的clk,有输出输入的,
32K_CLK, SYS_CLK, L3_ICLK, L4_ICLK, DSS_TV_CLK, 48M_CLK, 12, 96, ....
通过DPLL输出了好几个供外设使用的频率。所以需要有模块来控制这些DPLL的输出,使能等等。


1:公共模块代码部分。是这一系列平台共通的代码。
主要结构: clk, clk_functions, clk_notifier
结构: clockdomain,
全局的平台结构相关的clk操作函数集: arch_clk
全局的clk结构链表:  clocks
全局的clk_notifier结构链表: clk_notifier_list

模块中提供操作函数: 注册,注销(clk, clk_notifier),等等。
注册: 初始化相关结构, 添加到全局链表中, 调用arch_clk对应的操作函数(平台结构相关的操作.)
基本上本模块中导出的函数接口,都会调用到arch_clk中某个函数.以实现平台相关的clk相关操作。

arch_clk的初始化接口:
int __init clk_init(struct clk_functions * custom_clocks)
{
if (!custom_clocks) {
printk(KERN_ERR "No custom clock functions registered\n");
BUG();
}

arch_clock = custom_clocks;

return 0;
}
2:具体平台板代码实现。
clk_init(&omap2_clk_functions);
static struct clk_functions omap2_clk_functions = {
.clk_register= omap2_clk_register,
.clk_enable= omap2_clk_enable,
.clk_disable= omap2_clk_disable,
.clk_round_rate= omap2_clk_round_rate,
.clk_round_rate_parent= omap2_clk_round_rate_parent,
.clk_set_rate= omap2_clk_set_rate,
.clk_set_parent= omap2_clk_set_parent,
.clk_get_parent= omap2_clk_get_parent,
.clk_disable_unused= omap2_clk_disable_unused,
#ifdef CONFIG_CPU_FREQ
.clk_init_cpufreq_table = omap2_clk_init_cpufreq_table,
#endif
};

初始化clk模型:
static struct clk *onchip_34xx_clks[] __initdata = {
。。。。
}
int __init omap2_clk_init(void)
{
...
clk_init(&omap2_clk_functions);//注册板相关的clk操作集。
clk_register(*clkp);//注册所有的板上时钟。
recalculate_root_clocks(); //重新计算所有的已注册 的时钟的 频率
/*
* Only enable those clocks we will need, let the drivers
* enable other clocks as necessary
*/
clk_enable_init_clocks(); //打开我们现在要使用的时钟。即那些有(ALWAYS_ENABLED)父时钟的时钟。
==> if (clkp->flags & ENABLE_ON_INIT)  clk_enable(clkp);
}

//**************DPLL 数字锁相环, 分频?*************************************
struct clksel_rate {
u32  val;//the respected register's value.这个分频方式下,寄存器的值
u8  div;//the divisor  parent.rate/div。 分频方式 , 除数。
u8  flags;
};
设置频率:(由父频率分频得到/ 通过设置寄存器的值,可以使输出的频率改变, parent.rate / div)
int omap2_clksel_set_rate(struct clk *clk, unsigned long rate)//通过写对应的寄存器来控制这个CLK的频率。
validrate = omap2_clksel_round_rate_div(clk, rate, &new_div);//==>test_rate = clk->parent->rate / clkr->div;
if (validrate != rate)
      return -EINVAL;
clk->rate = clk->parent->rate / new_div;//这里表明了父时钟与子时钟间的频率关系。
_omap2xxx_clk_commit(clk);//最后 会调用这个,有点像linux的barrier,即使刚才的频率调整生效。



//************************************* arm 的频率 源 定义********************************************************/
arm core:
static const struct clksel osc_sys_clksel[] = {
{ .parent = &virt_12m_ck,   .rates = osc_sys_12m_rates },
{ .parent = &virt_13m_ck,   .rates = osc_sys_13m_rates },
{ .parent = &virt_16_8m_ck, .rates = osc_sys_16_8m_rates },
{ .parent = &virt_19_2m_ck, .rates = osc_sys_19_2m_rates },
{ .parent = &virt_26m_ck,   .rates = osc_sys_26m_rates },
{ .parent = &virt_38_4m_ck, .rates = osc_sys_38_4m_rates },
{ .parent = NULL },
};

/* Oscillator clock */
/* 12, 13, 16.8, 19.2, 26, or 38.4 MHz */
static struct clk osc_sys_ck = {
.name  = "osc_sys_ck",
.prcm_mod= OMAP3430_CCR_MOD | CLK_REG_IN_PRM,
.init  = &omap2_init_clksel_parent,
.clksel_reg= OMAP3_PRM_CLKSEL_OFFSET,
.clksel_mask= OMAP3430_SYS_CLKIN_SEL_MASK,
.clksel  = osc_sys_clksel,
/* REVISIT: deal with autoextclkmode? */
.flags  = CLOCK_IN_OMAP343X | ALWAYS_ENABLED,
.clkdm  = { .name = "prm_clkdm" },
.recalc  = &omap2_clksel_recalc,
};
/* Latency: this clock is only enabled after PRM_CLKSETUP.SETUP_TIME */
/* Feeds DPLLs - divided first by PRM_CLKSRC_CTRL.SYSCLKDIV? */
static struct clk sys_ck = {
.name  = "sys_ck",
.parent  = &osc_sys_ck,
.prcm_mod= OMAP3430_GR_MOD | CLK_REG_IN_PRM,
.init  = &omap2_init_clksel_parent,
.clksel_reg= OMAP3_PRM_CLKSRC_CTRL_OFFSET,
.clksel_mask= OMAP_SYSCLKDIV_MASK,
.clksel  = sys_clksel,
.flags  = CLOCK_IN_OMAP343X | ALWAYS_ENABLED,
.clkdm  = { .name = "prm_clkdm" },
.recalc  = &omap2_clksel_recalc,
};
/* DPLL1 */
/* MPU clock source */
/* Type: DPLL */
static struct dpll_data dpll1_dd = {
.mult_div1_reg= OMAP3430_CM_CLKSEL1_PLL,
.mult_mask= OMAP3430_MPU_DPLL_MULT_MASK,
.div1_mask= OMAP3430_MPU_DPLL_DIV_MASK,
.freqsel_mask= OMAP3430_MPU_DPLL_FREQSEL_MASK,
.control_reg= OMAP3430_CM_CLKEN_PLL,
.enable_mask= OMAP3430_EN_MPU_DPLL_MASK,
.modes  = (1 << DPLL_LOW_POWER_BYPASS) | (1 << DPLL_LOCKED),
.auto_recal_bit= OMAP3430_EN_MPU_DPLL_DRIFTGUARD_SHIFT,
.recal_en_bit= OMAP3430_MPU_DPLL_RECAL_EN_SHIFT,
.recal_st_bit= OMAP3430_MPU_DPLL_ST_SHIFT,
.autoidle_reg= OMAP3430_CM_AUTOIDLE_PLL,
.autoidle_mask= OMAP3430_AUTO_MPU_DPLL_MASK,
.idlest_reg= OMAP3430_CM_IDLEST_PLL,
.idlest_mask= OMAP3430_ST_MPU_CLK_MASK,
.bypass_clk= &dpll1_fck,
.max_multiplier = OMAP3_MAX_DPLL_MULT,
.min_divider= 1,
.max_divider= OMAP3_MAX_DPLL_DIV,
.rate_tolerance = DEFAULT_DPLL_RATE_TOLERANCE
};
static struct clk dpll1_ck = {
.name  = "dpll1_ck",
.parent  = &sys_ck,
.prcm_mod= MPU_MOD,
.dpll_data= &dpll1_dd,
.flags  = CLOCK_IN_OMAP343X | ALWAYS_ENABLED | RECALC_ON_ENABLE,
.round_rate= &omap2_dpll_round_rate,
.set_rate= &omap3_noncore_dpll_set_rate,
.clkdm  = { .name = "dpll1_clkdm" },
.recalc  = &omap3_dpll_recalc,
};
/*
 * This virtual clock provides the CLKOUTX2 output from the DPLL if the
 * DPLL isn't bypassed.
 */
static struct clk dpll1_x2_ck = {
.name  = "dpll1_x2_ck",
.parent  = &dpll1_ck,
.flags  = CLOCK_IN_OMAP343X | PARENT_CONTROLS_CLOCK,
.clkdm  = { .name = "dpll1_clkdm" },
.recalc  = &omap3_clkoutx2_recalc,
};

/*
 * Does not exist in the TRM - needed to separate the M2 divider from
 * bypass selection in mpu_ck
 */
static struct clk dpll1_x2m2_ck = {
.name  = "dpll1_x2m2_ck",
.parent  = &dpll1_x2_ck,
.prcm_mod= MPU_MOD,
.init  = &omap2_init_clksel_parent,
.clksel_reg= OMAP3430_CM_CLKSEL2_PLL,
.clksel_mask= OMAP3430_MPU_DPLL_CLKOUT_DIV_MASK,
.clksel  = div16_dpll1_x2m2_clksel,
.flags  = CLOCK_IN_OMAP343X | PARENT_CONTROLS_CLOCK,
.clkdm  = { .name = "dpll1_clkdm" },
.recalc  = &omap2_clksel_recalc,
};

static struct clk mpu_ck = {
.name  = "mpu_ck",
.parent  = &dpll1_x2m2_ck,
.flags  = CLOCK_IN_OMAP343X | PARENT_CONTROLS_CLOCK,
.clkdm  = { .name = "mpu_clkdm" },
.recalc  = &followparent_recalc,
};
/* arm_fck is divided by two when DPLL1 locked; otherwise, passthrough mpu_ck */
static const struct clksel_rate arm_fck_rates[] = {
{ .div = 1, .val = 0, .flags = RATE_IN_343X | DEFAULT_RATE },
{ .div = 2, .val = 1, .flags = RATE_IN_343X },
{ .div = 0 },
};

static const struct clksel arm_fck_clksel[] = {
{ .parent = &mpu_ck, .rates = arm_fck_rates },
{ .parent = NULL }
};
static struct clk arm_fck = {
.name  = "arm_fck",
.parent  = &mpu_ck,
.prcm_mod= MPU_MOD,
.init  = &omap2_init_clksel_parent,
.clksel_reg= OMAP3430_CM_IDLEST_PLL,
.clksel_mask= OMAP3430_ST_MPU_CLK_MASK,
.clksel  = arm_fck_clksel,
.flags  = CLOCK_IN_OMAP343X | PARENT_CONTROLS_CLOCK,
.clkdm  = { .name = "mpu_clkdm" },
.recalc  = &omap2_clksel_recalc,
};


//*********************************************************************************************/

struct clk_notifier_data {
struct clk*clk;
unsigned longold_rate;
unsigned longnew_rate;
};
//时钟结构。
struct clk {
struct list_headnode;
const char*name;
int  id;
struct clk*parent;
unsigned longrate;
unsigned longtemp_rate;
struct list_headchildren;
__u32  flags;
u32  enable_reg;
void  (*recalc)(struct clk *, unsigned long, u8);
int  (*set_rate)(struct clk *, unsigned long);
long  (*round_rate)(struct clk *, unsigned long);
void  (*init)(struct clk *);
int  (*enable)(struct clk *);
void  (*disable)(struct clk *);
u16  notifier_count;
__u8  enable_bit;
__s8  usecount;
u8  idlest_bit;
#if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3)
u8  fixed_div;
u32  clksel_mask;
const struct clksel*clksel;
struct dpll_data*dpll_data;
union {
const char*name;
struct clockdomain*ptr;
} clkdm;
u16  clksel_reg;
s16  prcm_mod;
#else
__u8  rate_offset;
__u8  src_offset;
#endif
#if defined(CONFIG_PM_DEBUG) && defined(CONFIG_DEBUG_FS)
struct dentry*dent;/* For visible tree hierarchy */
#endif
};

struct clk_functions {
int  (*clk_register)(struct clk *clk);
int  (*clk_enable)(struct clk *clk);
void  (*clk_disable)(struct clk *clk);
long  (*clk_round_rate)(struct clk *clk, unsigned long rate);
long  (*clk_round_rate_parent)(struct clk *clk,
struct clk *parent);
int  (*clk_set_rate)(struct clk *clk, unsigned long rate);
int  (*clk_set_parent)(struct clk *clk, struct clk *parent);
struct clk *(*clk_get_parent)(struct clk *clk);
void  (*clk_allow_idle)(struct clk *clk);
void  (*clk_deny_idle)(struct clk *clk);
void  (*clk_disable_unused)(struct clk *clk);
#ifdef CONFIG_CPU_FREQ
void  (*clk_init_cpufreq_table)(struct cpufreq_frequency_table **);
#endif
};
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 小孩感冒流清鼻涕怎么办 4岁宝宝半夜发烧怎么办 四岁宝宝免疫力低下怎么办 两岁宝宝咳嗽流鼻涕怎么办 小婴儿流清鼻涕怎么办 14个月宝宝流鼻涕怎么办 小孩一直流黄鼻涕怎么办 咳嗽有痰 浓鼻涕怎么办 儿童鼻窦炎总反复流脓鼻涕怎么办? 宝宝感冒咳嗽流黄鼻涕怎么办 哺乳期感冒流清鼻涕怎么办 哺乳期打喷嚏流清鼻涕怎么办 哺乳期妈妈感冒流清鼻涕怎么办 哺乳期严重流清鼻涕怎么办 小孩每天都是脓鼻涕怎么办 夏天小孩咳嗽流黄脓鼻涕怎么办? 受凉了流清鼻涕怎么办 宝宝50多天鼻塞怎么办 50多天孩子咳嗽怎么办 2岁宝宝伤风鼻塞怎么办 2个月伤风鼻塞怎么办 3个月宝宝鼻塞怎么办 感冒治好后咳嗽一直不好怎么办 天气太热感冒了怎么办 3个月的婴儿鼻塞怎么办 四个月宝宝感冒鼻塞严重怎么办 4个月小孩鼻塞怎么办 4个多月的宝宝流鼻涕怎么办 4个月大的宝宝流鼻涕怎么办 两岁宝宝着凉了怎么办 7岁儿童晚上鼻塞怎么办 儿童感冒鼻塞怎么办速效办法 7岁儿童感冒鼻塞怎么办 七个月婴儿感冒流鼻涕怎么办 婴儿感冒流鼻涕怎么办速效办法 三个月婴儿感冒咳嗽流鼻涕怎么办 五个月婴儿感冒咳嗽流鼻涕怎么办 两个多月的宝宝鼻塞怎么办 3个月宝宝感冒鼻塞怎么办 2个月宝宝感冒鼻塞怎么办 5个月宝宝鼻塞怎么办