【blog.csdn.net/lanmanck】
本文纯属啰嗦帖子,没有很高的技术含量,仅供参考。
以dm365的spi为例说明之:
1、先在dm365.c里定义几个宏:
定义PLL1/2的属性:static struct pll_data pll1_data{定义寄存器基址灯}
定义参考时钟:static struct clk ref_clk={定义晶振时钟}
定义PLL1的时钟:static struct clk pll1_clk={定义parent(父类)为ref_clk,data(属性)为pll1_data}
定义PLL1输出的时钟源,例如static struct clk pll1_sysclk6={定义parent(父类)为pll1_clk,}
定义PLL2的时钟:static struct clk pll2_clk={定义parent(父类)为ref_clk,data(属性)为pll2_data}
定义PLL2输出的时钟源,例如static struct clk pll2_sysclk3={定义parent(父类)为pll2_clk,}
2、定义外设使用的PLL输出源:
static struct clk vpss_dac_clk={定义parent为pll1_sysclk3,LPSC=DM365_LPSC_DAC_CLK}
static struct clk spi2_clk={定义parent为pll1_sysclk4,LPSC=DM365_LPSC_SPI2}
3、定义参考时钟、pll时钟、pll输出源的查找表:
static struct clk_lookup dm365_clks[]={例如CLK("spi_davinci.2",NULL,&spi2_clk)}
4、查找表赋值给struct davinci_soc_info davinci_soc_info_dm365.cpu_clks
5、系统注册时调用davinci_common_init(struct davinci_soc_info *a),此函数会判断.cpu_clks是否有效,
如果有效,则调用davinci_clk_init进行clock初始化,看看davinci_clk_init()都干了些啥
6、davinci_clk_init() 挨个遍历查找表的clk(最后一位成员)
如果查找表中定义的clk->recalc为0(一般都为0),则赋值重计算的api。
例如找到pll1_clk,发现pll_data为pll1_data,则赋值为clk->recalc = clk_pllclk_recalc。
又如,找到标志位clk->flags & CLK_PLL,表示这个clk是PLL输出源,则赋值为clk->recalc = clk_sysclk_recalc。
又如,找到clk有父类clk->parent,表示这个是个PSC时钟,赋值为clk_recalc = clk_leafclk_recalc
因为这几个if有优先级,所以基本上顺序为:PLL、PLL输出源、外设时钟
接着,如果这个clk是PLL,则ioremap这个PLL的寄存器基址,用来后面的配置
现在,调用clk->recalc()来获得当前clk的实际时钟:clk->rate = clk->recalc(clk);
如果时钟管理有效(clk->lpsc),则把标志位置上clk->flags |= CLK_PSC;
调用clk_register(clk)注册该时钟到内核中进行管理
最后如果标志位flags有ALWAYS_ENABLED,则使能该clk:clk_enable(clk);
7、实例:SPI2就是调用clk_leafclk_recalc()来得到自己的bus clock,实际上该函数返回的就是parent的clock,
我们看到spi2_clk定义时parent=pll1_sysclk4,相当于spi2的bus时钟就是pll1_sysclk4,而这个时钟是通过
clk_sysclk_recalc()算出来的。
OK, 就这么多!