您的位置:首页 > 其它

struct clk clk-get() clk_enable();

2014-05-27 16:45 316 查看
struct clk; 之ARM

struct clk {

struct list_head list;

struct module *owner;

struct clk *parent;

const char *name;

int id;

int usage;

unsigned long rate;

unsigned long ctrlbit;

int (*enable)(struct clk *, int enable);

int (*set_rate)(struct clk *c, unsigned long rate);

unsigned long (*get_rate)(struct clk *c);

unsigned long (*round_rate)(struct clk *c, unsigned long rate);

int (*set_parent)(struct clk *c, struct clk *parent);

};

************************************************

//arm/mach-3c2440/mach-mini2440.c

static void __init mini2440_map_io(void)

{

s3c24xx_init_io(mini2440_iodesc, ARRAY_SIZE(mini2440_iodesc));

s3c24xx_init_clocks(12000000);//时钟的初始化

s3c24xx_init_uarts(mini2440_uartcfgs, ARRAY_SIZE(mini2440_uartcfgs));

}

************************************************

************************************************

//arm/plat-s3c/init.c

void __init s3c24xx_init_clocks(int xtal)

{

if (xtal == 0)

xtal = 12*1000*1000;

if (cpu == NULL)

panic("s3c24xx_init_clocks: no cpu setup?\n");

if (cpu->init_clocks == NULL)

panic("s3c24xx_init_clocks: cpu has no clock init\n");

else

(cpu->init_clocks)(xtal); //struct cpu_table

}

^^^^^^^

struct cpu_table { //cpu的结构

unsigned long idcode;

unsigned long idmask;

void (*map_io)(void);

void (*init_uarts)(struct s3c2410_uartcfg *cfg, int no);

void (*init_clocks)(int xtal);

int (*init)(void);

const char *name;

};

static struct cpu_table cpu_ids[] __initdata = { //各个型号的信息与初始化函数

{

.idcode = 0x32410000,

.idmask = 0xffffffff,

.map_io = s3c2410_map_io,

.init_clocks = s3c2410_init_clocks,

.init_uarts = s3c2410_init_uarts,

.init = s3c2410_init,

.name = name_s3c2410

},

{

.idcode = 0x32410002,

.idmask = 0xffffffff,

.map_io = s3c2410_map_io,

.init_clocks = s3c2410_init_clocks,

.init_uarts = s3c2410_init_uarts,

.init = s3c2410a_init,

.name = name_s3c2410a

},

{

.idcode = 0x32440000,

.idmask = 0xffffffff,

.map_io = s3c244x_map_io,

.init_clocks = s3c244x_init_clocks,

.init_uarts = s3c244x_init_uarts,

.init = s3c2440_init,

.name = name_s3c2440

},

{

.idcode = 0x32440001,

.idmask = 0xffffffff,

.map_io = s3c244x_map_io,

.init_clocks = s3c244x_init_clocks,

.init_uarts = s3c244x_init_uarts,

.init = s3c2440_init,

.name = name_s3c2440a

},

{

.idcode = 0x32440aaa,

.idmask = 0xffffffff,

.map_io = s3c244x_map_io,

.init_clocks = s3c244x_init_clocks,

.init_uarts = s3c244x_init_uarts,

.init = s3c2442_init,

.name = name_s3c2442

},

{

.idcode = 0x32440aab,

.idmask = 0xffffffff,

.map_io = s3c244x_map_io,

.init_clocks = s3c244x_init_clocks,

.init_uarts = s3c244x_init_uarts,

.init = s3c2442_init,

.name = name_s3c2442b

},

{

.idcode = 0x32412001,

.idmask = 0xffffffff,

.map_io = s3c2412_map_io,

.init_clocks = s3c2412_init_clocks,

.init_uarts = s3c2412_init_uarts,

.init = s3c2412_init,

.name = name_s3c2412,

},

{ /* a newer version of the s3c2412 */

.idcode = 0x32412003,

.idmask = 0xffffffff,

.map_io = s3c2412_map_io,

.init_clocks = s3c2412_init_clocks,

.init_uarts = s3c2412_init_uarts,

.init = s3c2412_init,

.name = name_s3c2412,

},

{

.idcode = 0x32443001,

.idmask = 0xffffffff,

.map_io = s3c2443_map_io,

.init_clocks = s3c2443_init_clocks,

.init_uarts = s3c2443_init_uarts,

.init = s3c2443_init,

.name = name_s3c2443,

},

{

.idcode = 0x0, /* S3C2400 doesn't have an idcode */

.idmask = 0xffffffff,

.map_io = s3c2400_map_io,

.init_clocks = s3c2400_init_clocks,

.init_uarts = s3c2400_init_uarts,

.init = s3c2400_init,

.name = name_s3c2400

},

};

************************************************

*******************************************************************************************

/arm/plat-s3c24xx/s3c244x.c

void __init s3c244x_init_clocks(int xtal)

{

/* initialise the clocks here, to allow other things like the

* console to use them, and to add new ones after the initialisation

*/

s3c24xx_register_baseclocks(xtal);//注册时钟

s3c244x_setup_clocks();

s3c2410_baseclk_add();

}

int __init s3c24xx_register_baseclocks(unsigned long xtal)

{

printk(KERN_INFO "S3C24XX Clocks, (c) 2004 Simtec Electronics\n");

clk_xtal.rate = xtal;

/* register our clocks */

if (s3c24xx_register_clock(&clk_xtal) < 0) //基时钟

printk(KERN_ERR "failed to register master xtal\n");

if (s3c24xx_register_clock(&clk_mpll) < 0) //mpll clk时钟

printk(KERN_ERR "failed to register mpll clock\n");

if (s3c24xx_register_clock(&clk_upll) < 0) //upll clk时钟

printk(KERN_ERR "failed to register upll clock\n");

if (s3c24xx_register_clock(&clk_f) < 0) // FCLK时钟

printk(KERN_ERR "failed to register cpu fclk\n");

if (s3c24xx_register_clock(&clk_h) < 0) //HCLK时钟

printk(KERN_ERR "failed to register cpu hclk\n");

if (s3c24xx_register_clock(&clk_p) < 0) //PCLK时钟

printk(KERN_ERR "failed to register cpu pclk\n");

return 0;

}

加入链表中,统一管理,这里只是注册,还没有填充具体的值,下面这个函数就是获取需要的值

void __init_or_cpufreq s3c244x_setup_clocks(void)//设置时钟频率

{

struct clk *xtal_clk;

unsigned long clkdiv;

unsigned long camdiv;

unsigned long xtal;

unsigned long hclk, fclk, pclk;

int hdiv = 1;

xtal_clk = clk_get(NULL, "xtal");

xtal = clk_get_rate(xtal_clk);

clk_put(xtal_clk);

fclk = s3c24xx_get_pll(__raw_readl(S3C2410_MPLLCON), xtal) * 2;//获得fclk时钟频率

clkdiv = __raw_readl(S3C2410_CLKDIVN);

camdiv = __raw_readl(S3C2440_CAMDIVN);

/* work out clock scalings */

switch (clkdiv & S3C2440_CLKDIVN_HDIVN_MASK) { //获取时钟分配值

case S3C2440_CLKDIVN_HDIVN_1:

hdiv = 1;

break;

case S3C2440_CLKDIVN_HDIVN_2:

hdiv = 2;

break;

case S3C2440_CLKDIVN_HDIVN_4_8:

hdiv = (camdiv & S3C2440_CAMDIVN_HCLK4_HALF) ? 8 : 4;

break;

case S3C2440_CLKDIVN_HDIVN_3_6:

hdiv = (camdiv & S3C2440_CAMDIVN_HCLK3_HALF) ? 6 : 3;

break;

}

hclk = fclk / hdiv;

pclk = hclk / ((clkdiv & S3C2440_CLKDIVN_PDIVN) ? 2 : 1);

/* print brief summary of clocks, etc */

printk("S3C244X: core %ld.%03ld MHz, memory %ld.%03ld MHz, peripheral %ld.%03ld MHz\n",

print_mhz(fclk), print_mhz(hclk), print_mhz(pclk));

s3c24xx_setup_clocks(fclk, hclk, pclk);//设置时钟值,就是填充一直以前注册的时钟

}

上面这个函数完成了对时钟频率的统计到获取

***************************************************

int __init s3c2410_baseclk_add(void)//注册其他的时钟

{

unsigned long clkslow = __raw_readl(S3C2410_CLKSLOW);

unsigned long clkcon = __raw_readl(S3C2410_CLKCON);

struct clk *clkp;

struct clk *xtal;

int ret;

int ptr;

clk_upll.enable = s3c2410_upll_enable;

if (s3c24xx_register_clock(&clk_usb_bus) < 0)

printk(KERN_ERR "failed to register usb bus clock\n");

/* register clocks from clock array */

clkp = init_clocks;

for (ptr = 0; ptr < ARRAY_SIZE(init_clocks); ptr++, clkp++) {

/* ensure that we note the clock state */

clkp->usage = clkcon & clkp->ctrlbit ? 1 : 0;

ret = s3c24xx_register_clock(clkp);

if (ret < 0) {

printk(KERN_ERR "Failed to register clock %s (%d)\n",

clkp->name, ret);

}

}

/* We must be careful disabling the clocks we are not intending to

* be using at boot time, as subsystems such as the LCD which do

* their own DMA requests to the bus can cause the system to lockup

* if they where in the middle of requesting bus access.

*

* Disabling the LCD clock if the LCD is active is very dangerous,

* and therefore the bootloader should be careful to not enable

* the LCD clock if it is not needed.

*/

/* install (and disable) the clocks we do not need immediately */

clkp = init_clocks_disable;

for (ptr = 0; ptr < ARRAY_SIZE(init_clocks_disable); ptr++, clkp++) {

ret = s3c24xx_register_clock(clkp);

if (ret < 0) {

printk(KERN_ERR "Failed to register clock %s (%d)\n",

clkp->name, ret);

}

s3c2410_clkcon_enable(clkp, 0);

}

/* show the clock-slow value */

xtal = clk_get(NULL, "xtal");

printk("CLOCK: Slow mode (%ld.%ld MHz), %s, MPLL %s, UPLL %s\n",

print_mhz(clk_get_rate(xtal) /

( 2 * S3C2410_CLKSLOW_GET_SLOWVAL(clkslow))),

(clkslow & S3C2410_CLKSLOW_SLOW) ? "slow" : "fast",

(clkslow & S3C2410_CLKSLOW_MPLL_OFF) ? "off" : "on",

(clkslow & S3C2410_CLKSLOW_UCLK_OFF) ? "off" : "on");

s3c_pwmclk_init(); //pwm的时钟有些特别,所以单独初始化;(有两个时钟源可选)

return 0;

}

从上面的这几个函数可以看,平常的和clk_get(,);clk_enable();就是这个原因,不让咱们直接操作寄存器,而采用统一管理的模式,增加了安全性与可靠性。

注1:

Mpll=(2*m*Fin)(p*2^s)

m=M(分频系数M)+8, p=P(分频系数P)+2

集成电路UPLL的工作原理与MPLL相同。

M、P、S的值由PLL控制寄存器(MPLLCON和UPLLCON)来设置。

注2:

USB host接口和USB设备接口提供正常工作需要的48MHz时钟,UCLK主要有4个状态:

1)刚刚复位时,UCLK为外部晶振或外部时钟。

2)配置UPLL结束后,在UPLL稳定时间内,UCLK为低电平,UPLL稳定时间结束,UCLK为48MHz。

3)CLKSLOW寄存器关闭UPLL,UCLK为外部晶振或者外部时钟。

4)CLKSLOW寄存器打开UPLL,UCLK为48MHz。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: