给你指一个方向或者说是方法, 可以提高自己编程kernel/driver/u-boot的能力,或者说写code的能力。
举个例子,
比如说 kernel 里面的clock时钟的初始化部分, kernel的代码也不复杂, 但是我们要达到的目标就是 根据datasheet 能自己写出来,而不仅仅能看明白, 好比说 ,
会看小说的人,不见得都会写小说, 金庸只有一个。 我们现在就要作金庸。只有试着写 ,才能作为作家。
只是举个简单的例子:至少我是这样来练习的,
你可以对照你自己的u-boot或者omap5912 作类似的动作。 方法都是一样的。 总之, 就是截取现有的功能, 自己试着实现它, 久而久之, 能力就提高了。
看的时候, 对照着原来的driver 和datasheet ,了解到流程很简单,
1> 读取 MPLLCON 寄存器 , 比如读到 mpllcon_value 变量中,
2> 对 mpllcon_value 分别移位操作, 取得 MDIV ,PDIV ,SDIV
3> 利用公式:
Mpll = (m * Fin) / (p * 2^s)
m = (MDIV + 8), p = (PDIV + 2), s = SDIV
4> fclk = Mpll
5》 读取 CLKDIVN 寄存器 ,
6》 然后作相应的计算: 求出 FCLK , HCLK , PCLK (WATCHDOG 用到了,我们kernel就要初始化)
HDIVN | [1] | 0: HCLK has the clock same as the FCLK. | 0 |
|
| 1: HCLK has the clock same as the FCLK/2. |
|
PDIVN | [0] | 0: PCLK has the clock same as the HCLK. | 0 |
|
| 1: PCLK has the clock same as the HCLK/2. |
|
到现在流程已经知道了, 估计 2410自带的代码也忘记的差不多了,那就可以自己写了。 只有自己写, 才能发现自己的问题, 也才能领会 作者那样写的妙处,当然作者也不见得都对
我就自己重新实现了一个,
我自己写的时候, 就会考虑几个问题,
1> 我是要int 还是用 u32 ,会不会溢出呢?还有可移植性。
2> 小的临时变量 是用 u8 , 还是 u32
3> datasheet 有没有提到有些操作需要delay ,比如 delay(400) 什么的?
4> 最后 拿到板子上去验证。 看写的有没有问题?
自己实现的, 后面 有 2410 作者写的。 自己对比一下 。
static inline u32 s3c2410_get_pll_bob_version(u32 Fin, u32 mpllcon_value)
{
//u8 is enought for mdiv and pdiv and sdiv
u8 mdiv = 0; //作者都用
u8 pdiv = 0;
u8 sdiv = 0;
u32 mpll = 0;
//相比作者的代码, 这里面没有用 宏来标记一些bit 或者bitmask 。别人看起来可能一头污水, 不过标出来
//refer P240 CLOCK DIVIDER CONTROL (CLKDIVN) REGISTER 这样别人就一下子知道了。
mdiv = (mpllcon_value & ((1<<20) -1)) >> 12;
pdiv = (mpllcon_value & ((1<<10)-1)) >>4;
sdiv = (mpllcon_value & ((1<<2)-1)) >> 0; //我个人觉得 >> 0 很有意义。比不加强。
//see manual P237
mpll = (mdiv+8) * Fin;
mpll /=(pdiv+2);
mpll >>= sdiv;
printk("in s3c2410_get_mpll_bob_version() , mpll = %u\n",mpll);
return mpll;
}
void __init s3c2410_init_clocks(int xtal) //Õa¸öxtal¾íêÇ»ù±¾μÄêäèëÆμÂê12MHz
{
u32 mpll = 0;
u32 clock_mpllcon = 0;
u32 fclk = 0;
u32 hclk =0;
u32 pclk = 0;
u32 HDIVN1 = 0;
u32 HDIVN = 0;
u32 PDIVN = 0;
clock_mpllcon = ioread32(S3C2410_MPLLCON);
mpll = s3c2410_get_pll_bob_version((u32)xtal,clock_mpllcon);
fclk = mpll;
if(HDIVN1 = ioread32(S3C2410_CLKDIVN) & (1<<2))
{
hclk = pclk = fclk/4; //
}
else {
hclk = (HDIVN = ioread32(S3C2410_CLKDIVN) & (1<<1) )? fclk/2:fclk;
pclk = (PDIVN = ioread32(S3C2410_CLKDIVN) & (1<<0) )? hclk/2:hclk;
}
printk("fclk = %u, hclk=%u, pclk=%u\n",fclk,hclk,pclk);
。。。。
+++++++++++++++++++++++++++++++++++++++++++++++++++
然后再对照作者的程序的着两个函数, 对比 看看差距在哪里???只有找出差距, 才能提高自己!
static inline unsigned int
s3c2410_get_pll(unsigned int pllval, unsigned int baseclk)
{
unsigned int mdiv, pdiv, sdiv;
uint64_t fvco;
//作者用了一些宏 ,但是这么简单的操作 ,弄两次似乎不是很好。这就没什么说得了, 个人喜好不同, 不算差距。
mdiv = pllval >> S3C2410_PLLCON_MDIVSHIFT;
pdiv = pllval >> S3C2410_PLLCON_PDIVSHIFT;
sdiv = pllval >> S3C2410_PLLCON_SDIVSHIFT;
mdiv &= S3C2410_PLLCON_MDIVMASK;
pdiv &= S3C2410_PLLCON_PDIVMASK;
sdiv &= S3C2410_PLLCON_SDIVMASK;
fvco = (uint64_t)baseclk * (mdiv + 8);
do_div(fvco, (pdiv + 2) << sdiv);
return (unsigned int)fvco;
}
void __init s3c2410_init_clocks(int xtal)
{
unsigned long tmp;
unsigned long fclk;
unsigned long hclk;
unsigned long pclk; //干吗都声明成 unsigned long ,实际上 ,unsigned int 就足够了(当然int与long长度相同)
//更好的办法是 用 u32 来表示, 因为 32 bit 肯定够了。 作者的移植性不好。
/* now we've got our machine bits initialised, work out what
* clocks we've got */
fclk = s3c2410_get_pll(__raw_readl(S3C2410_MPLLCON), xtal); //用 __raw_readl() 更是勉强,事上 , kernel早就不推荐用 __raw_readl ()了, 应该 用 ioread32()
tmp = __raw_readl(S3C2410_CLKDIVN); //同上
/* work out clock scalings */
/* 这里功能上就少了一个: if(HDIVN1 = ioread32(S3C2410_CLKDIVN) & (1<<2)) ,按照datasheet :
HDIVN1 | [2] | Special bus clock ratio available. (1:4:4) 0: Reserved 1: HCLK has the clock same as the FCLK/4. PCLK has the clock same as the FCLK/4. Note: If this bit is "0b1", HDIVN and PDIVN must be set "0b0".
写程序的时候要体现出来这样的判断 | 0 |
HDIVN | [1] | 0: HCLK has the clock same as the FCLK. 1: HCLK has the clock same as the FCLK/2. | 0 |
PDIVN | [0] | 0: PCLK has the clock same as the HCLK. 1: PCLK has the clock same as the HCLK/2. | 0 |
应该要判断 bit[2] 的, 但是作者仅仅判断了 bit0和bit1 ,就是不对的。
*/
//少了对 HDIVN1 的判断。
hclk = fclk / ((tmp & S3C2410_CLKDIVN_HDIVN) ? 2 : 1);
pclk = hclk / ((tmp & S3C2410_CLKDIVN_PDIVN) ? 2 : 1);