linuxBSPmini2440之时钟

来源:互联网 发布:有特效的编程软件 编辑:程序博客网 时间:2024/06/14 06:58
 

内核时钟初始化函数是s3c24xx_init_clocks(12000000)。

改函数的从start_kernel-->setup_arch(&command_line)-->paging_init(mdesc)-->devicemaps_init(mdesc)-->if (mdesc->map_io)mdesc->map_io();-->mini2440_map_io-->s3c24xx_init_clocks(12000000).所以此函数是各个部分初始化的起点。

s3c24xx_init_clocks(12000000)定义如下:

--------------------------------------------------------------------------------------------------

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);
}

--------------------------------------------------------------------------------------------------

这个函数中看到一个cpu,那么这个变量是什么。看文件plat-s3c/init.c:

static struct cpu_table *cpu;它是一个cpu_table类型的指针。如下是其结构定义:

--------------------------------------------------------------------------------------------------

struct cpu_table {
 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;
};

--------------------------------------------------------------------------------------------------

这样就不难理解s3c24xx_init_clocks了。那么这个CPU指针具体指向什么呢。在文件cpu.c中有如下定义:

static const char name_s3c2400[]  = "S3C2400";
static const char name_s3c2410[]  = "S3C2410";
static const char name_s3c2412[]  = "S3C2412";
static const char name_s3c2440[]  = "S3C2440";
static const char name_s3c2442[]  = "S3C2442";
static const char name_s3c2442b[]  = "S3C2442B";
static const char name_s3c2443[]  = "S3C2443";
static const char name_s3c2410a[] = "S3C2410A";
static const char name_s3c2440a[] = "S3C2440A";

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,
 },
   
  .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,  
  .idmask  = 0xffffffff,
  .map_io  = s3c2400_map_io,
  .init_clocks = s3c2400_init_clocks,
  .init_uarts = s3c2400_init_uarts,
  .init  = s3c2400_init,
  .name  = name_s3c2400
 },
};

在这里,我们只关心.name为.name  = name_s3c2440的数组元素。这里.init_clocks部位NULL,那么函数s3c24xx_init_clocks中的(cpu->init_clocks)(xtal)将被运行。

接着就到了s3c244x.c中的函数s3c244x_init_clocks:

---------------------------------------------------------

void __init s3c244x_init_clocks(int xtal)
{
 

 s3c24xx_register_baseclocks(xtal);
 s3c244x_setup_clocks();
 s3c2410_baseclk_add();
}
--------------------------------------------------------------------------------------------------
s3c244x_init_clocks函数里,有三个函数

s3c24xx_register_baseclocks(xtal);这个函数用来注册clk_xtal,clk_mpll,clk_upll,clk_f,clk_h,clk_p六个clk类型的时钟。

s3c244x_setup_clocks()是时钟的设置。

s3c2410_baseclk_add()添加不同部件的时钟,以便驱动中使用。后面会相信分析。

下面一个一个来分析他们这三个函数到底做了什么工作。

s3c24xx_register_baseclocks(xtal)函数定义如下:

-------------------------------------------------

int __init s3c24xx_register_baseclocks(unsigned long xtal)
{
 printk(KERN_INFO "S3C24XX Clocks, (c) 2004 Simtec Electronics\n");

 clk_xtal.rate = xtal;

 

 if (s3c24xx_register_clock(&clk_xtal) < 0)
  printk(KERN_ERR "failed to register master xtal\n");

 if (s3c24xx_register_clock(&clk_mpll) < 0)
  printk(KERN_ERR "failed to register mpll clock\n");

 if (s3c24xx_register_clock(&clk_upll) < 0)
  printk(KERN_ERR "failed to register upll clock\n");

 if (s3c24xx_register_clock(&clk_f) < 0)
  printk(KERN_ERR "failed to register cpu fclk\n");

 if (s3c24xx_register_clock(&clk_h) < 0)
  printk(KERN_ERR "failed to register cpu hclk\n");

 if (s3c24xx_register_clock(&clk_p) < 0)
  printk(KERN_ERR "failed to register cpu pclk\n");

 return 0;
}
--------------------------------------------------------

就如上面所说的,这个函数通过调用s3c24xx_register_clock()一一注册了六个时钟变量clk_xtal,clk_mpll,clk_upll,clk_f,clk_h,clk_p。它们定义在文件plat-s3c的clock.c中:

struct clk clk_xtal = {
 .name  = "xtal",
 .id  = -1,
 .rate  = 0,
 .parent  = NULL,
 .ctrlbit = 0,
};

struct clk clk_ext = {
 .name  = "ext",
 .id  = -1,
};

struct clk clk_epll = {
 .name  = "epll",
 .id  = -1,
};

struct clk clk_mpll = {
 .name  = "mpll",
 .id  = -1,
 .set_rate = clk_default_setrate,
};

struct clk clk_upll = {
 .name  = "upll",
 .id  = -1,
 .parent  = NULL,
 .ctrlbit = 0,
};

struct clk clk_f = {
 .name  = "fclk",
 .id  = -1,
 .rate  = 0,
 .parent  = &clk_mpll,
 .ctrlbit = 0,
 .set_rate = clk_default_setrate,
};

struct clk clk_h = {
 .name  = "hclk",
 .id  = -1,
 .rate  = 0,
 .parent  = NULL,
 .ctrlbit = 0,
 .set_rate = clk_default_setrate,
};

struct clk clk_p = {
 .name  = "pclk",
 .id  = -1,
 .rate  = 0,
 .parent  = NULL,
 .ctrlbit = 0,
 .set_rate = clk_default_setrate,
};

struct clk结构体的定义如下:

---------------------------------------------------------------------------------------

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);
};

------------------------------------------------------------------------------------------

注册代码如下:

-------------------------------------------------------------------------------------

int s3c24xx_register_clock(struct clk *clk)
{
 if (clk->enable == NULL)
  clk->enable = clk_null_enable;

 

 
 BUG_ON(clk->list.prev != clk->list.next);

 spin_lock(&clocks_lock);
 list_add(&clk->list, &clocks);
 spin_unlock(&clocks_lock);

 return 0;
}
--------------------------------------------------------
主要看list_add(&clk->list,&clocks).系统中存在一个clocks的列表,这个就是通过list_add()把clk的list添加到这个列表中,从而注册。

 

s3c244x_setup_clocks()函数如下:

---------------------------------------------------------------------------

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;

 clkdiv = __raw_readl(S3C2410_CLKDIVN);
 camdiv = __raw_readl(S3C2440_CAMDIVN);

 

 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);

 

 printk("S3C244X: core %ld.ld MHz, memory %ld.ld MHz, peripheral %ld.ld MHz\n",
        print_mhz(fclk), print_mhz(hclk), print_mhz(pclk));

 s3c24xx_setup_clocks(fclk, hclk, pclk);
}
---------------------------------------------------------------------------

在这里有必要简单的说下s3c2440的时钟发生模块

linuxBSPmini2440之时钟

 

主时钟源来自外部晶振(XTIpll)或者外部时钟。用芯片引脚OM[3:2]来选择,一般情况下,OM3和OM2接地,这样,主时钟源即为外部晶振(此时USB时钟源也是外部晶振)。集成电路MPLL产生一个在频率和相位上同步于参考输入信号的输出信号,计算公式为:

Mpll=(2*m*Fin)(p*2^s)
m=M(分频系数M)+8, p=P(分频系数P)+2
        集成电路UPLL的工作原理与MPLL相同。
        M、P、S的值由PLL控制寄存器(MPLLCON和UPLLCON)来设置。
 
        图中的CLKCNTL模块主要由时钟控制寄存器CLKCON和慢速时钟控制寄存器CLKSLOW两个寄存器组成,一方面,它可以控制外围设备的时钟使能,从而实现功耗控制;另一方面,它还可以控制PLL,使FCLK=Mpll或者FCLK=输入时钟(外部晶振或者外部时钟)的分频。
 
        FCLK作为芯片核心ARM920T模块的时钟。
        HCLK也可以为ARM920T提供时钟(当CAMERA时钟分频寄存器的DVS_EN位为1时,HCLK作为ARM核心时钟),HCLK也为内存控制器、中断控制器、LCD控制器、DMA和USB HOST模块提供时钟。
        PCLK为WDT、IIS、IIC、PWM定时器、MMC接口、ADC、UART、GPIO、RTC和SPI提供时钟。
        FCLK、HCLK和PCLK之间的关系由时钟分频控制寄存器CLKDIVN(包括HDIVN和PDIVN两个部分)来决定。
 
        图中的USBCNTL模块主要是为USB host接口和USB设备接口提供正常工作需要的48MHz时钟,UCLK主要有4个状态:
1)刚刚复位时,UCLK为外部晶振或外部时钟。
2)配置UPLL结束后,在UPLL稳定时间内,UCLK为低电平,UPLL稳定时间结束,UCLK为48MHz。
3)CLKSLOW寄存器关闭UPLL,UCLK为外部晶振或者外部时钟。
4)CLKSLOW寄存器打开UPLL,UCLK为48MHz。
 
        图中的POWERCNTL模块主要实现功耗控制的功能。PLL稳定时间由稳定时间寄存器LOCKTIME来控制,此寄存器一般设为0xFFFFFFFF。
 
下面接着函数分析,在函数s3c244x_setup_clocks()中用到了clk_get,clk_get_rate,clk_put这些函数,这些都是对时钟机制的一个封装,对外提供的API接口。
在plat-s3c/clock.c中可以看到这些API的定义:
//传入参数dev和dev获取相应的模块的时钟clk结构体
struct clk *clk_get(struct device *dev, const char *id)
{
 struct clk *p;
 struct clk *clk = ERR_PTR(-ENOENT);
 int idno;
 if (dev == NULL || dev->bus != &platform_bus_type)
  idno = -1;
 else
  idno = to_platform_device(dev)->id;
 spin_lock(&clocks_lock);
 list_for_each_entry(p, &clocks, list) {
  if (p->id == idno &&
      strcmp(id, p->name) == 0 &&
      try_module_get(p->owner)) {
   clk = p;
   break;
  }
 }
 
 if (IS_ERR(clk)) {
  list_for_each_entry(p, &clocks, list) {
   if (p->id == -1 && strcmp(id, p->name) == 0 &&
       try_module_get(p->owner)) {
    clk = p;
    break;
   }
  }
 }
 spin_unlock(&clocks_lock);
 return clk;
}
void clk_put(struct clk *clk)
{
 module_put(clk->owner);
}
//启动时钟
int clk_enable(struct clk *clk)
{
 if (IS_ERR(clk) || clk == NULL)
  return -EINVAL;
 clk_enable(clk->parent);
 spin_lock(&clocks_lock);
 if ((clk->usage++) == 0)
  (clk->enable)(clk, 1);
 spin_unlock(&clocks_lock);
 return 0;
}
//禁止时钟,通过调用具体clk的disable
void clk_disable(struct clk *clk)
{
 if (IS_ERR(clk) || clk == NULL)
  return;
 spin_lock(&clocks_lock);
 if ((--clk->usage) == 0)
  (clk->enable)(clk, 0);
 spin_unlock(&clocks_lock);
 clk_disable(clk->parent);
}

unsigned long clk_get_rate(struct clk *clk)
{
 if (IS_ERR(clk))
  return 0;
 if (clk->rate != 0)
  return clk->rate;
 if (clk->get_rate != NULL)
  return (clk->get_rate)(clk);
 if (clk->parent != NULL)
  return clk_get_rate(clk->parent);
 return clk->rate;
}
long clk_round_rate(struct clk *clk, unsigned long rate)
{
 if (!IS_ERR(clk) && clk->round_rate)
  return (clk->round_rate)(clk, rate);
 return rate;
}
int clk_set_rate(struct clk *clk, unsigned long rate)
{
 int ret;
 if (IS_ERR(clk))
  return -EINVAL;
 
 WARN_ON(clk->set_rate == NULL);
 if (clk->set_rate == NULL)
  return -EINVAL;
 spin_lock(&clocks_lock);
 ret = (clk->set_rate)(clk, rate);
 spin_unlock(&clocks_lock);
 return ret;
}
struct clk *clk_get_parent(struct clk *clk)
{
 return clk->parent;
}
int clk_set_parent(struct clk *clk, struct clk *parent)
{
 int ret = 0;
 if (IS_ERR(clk))
  return -EINVAL;
 spin_lock(&clocks_lock);
 if (clk->set_parent)
  ret = (clk->set_parent)(clk, parent);
 spin_unlock(&clocks_lock);
 return ret;
}
EXPORT_SYMBOL(clk_get);
EXPORT_SYMBOL(clk_put);
EXPORT_SYMBOL(clk_enable);
EXPORT_SYMBOL(clk_disable);
EXPORT_SYMBOL(clk_get_rate);
EXPORT_SYMBOL(clk_round_rate);
EXPORT_SYMBOL(clk_set_rate);
EXPORT_SYMBOL(clk_get_parent);
EXPORT_SYMBOL(clk_set_parent);
上面的函数比较简单,都是在clocks这个列表中通过一定的条件获得clk,或者通过获得的clk调用clk结构体里面的定义函数完成相应的功能。
xtal_clk = clk_get(NULL, "xtal");获取xtal即描述晶振的clk结构体。xtal = clk_get_rate(xtal_clk);获得晶振的频率为12MHz,我们可以看到在s3c24xx_register_baseclocks函数中clk_xtal.rate = xtal;实现赋值。
clk_put(xtal_clk);不太理解。好像与模块有关。
fclk = s3c24xx_get_pll(__raw_readl(S3C2410_MPLLCON), xtal) * 2;这个函数通过读取寄存器值在通过公式计算出FCLK的值具体的计算方法看看datasheet。
然后clkdiv = __raw_readl(S3C2410_CLKDIVN);
 camdiv = __raw_readl(S3C2440_CAMDIVN);获得了寄存器CLKDIVN和CAMDIVN的值
寄存器描述如下:
linuxBSPmini2440之时钟
linuxBSPmini2440之时钟
所以接下来的switch()语句得到一个hdiv值来进一步获得hclk和pclk的频率。这两个频率相当重要,作用上面已经提到。最后一个是s3c24xx_setup_clocks(),不知道你们又没留心看有上面提到的clk_upll,clk_mpll,clk_h,clk_f没有初始化他们的频率,那么s3c24xx_setup_clocks()正是起到了这样的一个作用。s3c244x_setup_clocks()函数分析完了,接下来是s3c2410_baseclk_add()函数。从这个函数表面的意思来看,是添加一些基本的时钟。的确如此。这些时钟是一些抽象的概念,板上一个设备对应相应的一个时钟。从本质上看,这些时钟其实都是pclk,hclk,fclk等。只不过是面向对象话得抽象了。那么这些时钟有哪里呢。都是些什么。
文件s3c2410-clock.c中定义了两个数组static struct clk init_clocks[] ,static struct clk init_clocks_disable[]。
---------------------------------------------------------------------------------------
static struct clk init_clocks_disable[] = {
 {
  .name  = "nand",
  .id  = -1,
  .parent  = &clk_h,
  .enable  = s3c2410_clkcon_enable,
  .ctrlbit = S3C2410_CLKCON_NAND,
 }, {
  .name  = "sdi",
  .id  = -1,
  .parent  = &clk_p,
  .enable  = s3c2410_clkcon_enable,
  .ctrlbit = S3C2410_CLKCON_SDI,
 }, {
  .name  = "adc",
  .id  = -1,
  .parent  = &clk_p,
  .enable  = s3c2410_clkcon_enable,
  .ctrlbit = S3C2410_CLKCON_ADC,
 }, {
  .name  = "i2c",
  .id  = -1,
  .parent  = &clk_p,
  .enable  = s3c2410_clkcon_enable,
  .ctrlbit = S3C2410_CLKCON_IIC,
 }, {
  .name  = "iis",
  .id  = -1,
  .parent  = &clk_p,
  .enable  = s3c2410_clkcon_enable,
  .ctrlbit = S3C2410_CLKCON_IIS,
 }, {
  .name  = "spi",
  .id  = -1,
  .parent  = &clk_p,
  .enable  = s3c2410_clkcon_enable,
  .ctrlbit = S3C2410_CLKCON_SPI,
 }
};
static struct clk init_clocks[] = {
 {
  .name  = "lcd",
  .id  = -1,
  .parent  = &clk_h,
  .enable  = s3c2410_clkcon_enable,
  .ctrlbit = S3C2410_CLKCON_LCDC,
 }, {
  .name  = "gpio",
  .id  = -1,
  .parent  = &clk_p,
  .enable  = s3c2410_clkcon_enable,
  .ctrlbit = S3C2410_CLKCON_GPIO,
 }, {
  .name  = "usb-host",
  .id  = -1,
  .parent  = &clk_h,
  .enable  = s3c2410_clkcon_enable,
  .ctrlbit = S3C2410_CLKCON_USBH,
 }, {
  .name  = "usb-device",
  .id  = -1,
  .parent  = &clk_h,
  .enable  = s3c2410_clkcon_enable,
  .ctrlbit = S3C2410_CLKCON_USBD,
 }, {
  .name  = "timers",
  .id  = -1,
  .parent  = &clk_p,
  .enable  = s3c2410_clkcon_enable,
  .ctrlbit = S3C2410_CLKCON_PWMT,
 }, {
  .name  = "uart",
  .id  = 0,
  .parent  = &clk_p,
  .enable  = s3c2410_clkcon_enable,
  .ctrlbit = S3C2410_CLKCON_UART0,
 }, {
  .name  = "uart",
  .id  = 1,
  .parent  = &clk_p,
  .enable  = s3c2410_clkcon_enable,
  .ctrlbit = S3C2410_CLKCON_UART1,
 }, {
  .name  = "uart",
  .id  = 2,
  .parent  = &clk_p,
  .enable  = s3c2410_clkcon_enable,
  .ctrlbit = S3C2410_CLKCON_UART2,
 }, {
  .name  = "rtc",
  .id  = -1,
  .parent  = &clk_p,
  .enable  = s3c2410_clkcon_enable,
  .ctrlbit = S3C2410_CLKCON_RTC,
 }, {
  .name  = "watchdog",
  .id  = -1,
  .parent  = &clk_p,
  .ctrlbit = 0,
 }, {
  .name  = "usb-bus-host",
  .id  = -1,
  .parent  = &clk_usb_bus,
 }, {
  .name  = "usb-bus-gadget",
  .id  = -1,
  .parent  = &clk_usb_bus,
 },
}; 
--------------------------------------------------------------------------------------------------
这两个又什么区别呢,init_clocks_disable[]数组里面的时钟在系统启动的时候要关闭的,而init_clocks里面的是要系统启动要初始化的。所以在启动时间我们要谨慎的关闭我们哪些不需要的时钟。就像LCD,如果启动的时候启动时钟打开DMA请求,那么就会出问题。相反启动过后LCD运行着关闭他的时钟也是很危险的。
下面是s3c2410_baseclk_add()的源码:
--------------------------------------------------------------------------------------------
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");
 
 clkp = init_clocks;
 for (ptr = 0; ptr < ARRAY_SIZE(init_clocks); ptr++, clkp++) {
  
  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);
  }
 }
 
 
 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);
 }
 
 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();
 return 0;
}
---------------------------------------------------------------------------------------------
两个for语句分别安装了上面提到的两个数组的时钟,相关的初始化结构体。赋值了clk_upll.enable。最后面初始化和注册了PWM时钟。PWM时钟的注册和其他分开始因为它比较特别。
未完待续......