参考链接:http://blog.sina.com.cn/s/blog_753fd0b00100t9u7.html
内核时钟初始化函数是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
},
};
怎么确定是调用哪一个.init_clocks回调函数?
start_kernel-->setup_arch(&command_line)-->paging_init(mdesc)-->devicemaps_init(mdesc)-->if (mdesc->map_io)mdesc->map_io();-->mini2440_map_io->s3c24xx_init_io(mini2440_iodesc, ARRAY_SIZE(mini2440_iodesc)) -->s3c24xx_init_io
来看s3c24xx_init_io这个函数
-
void __init s3c24xx_init_io(struct map_desc *mach_desc, int size)
-
{
-
unsigned long idcode = 0x0;
-
-
/* initialise the io descriptors we need for initialisation */
-
iotable_init(mach_desc, size);//开发板层次的静态映射
-
iotable_init(s3c_iodesc, ARRAY_SIZE(s3c_iodesc));//最小系统层次的静态映射
-
-
if (cpu_architecture() >= CPU_ARCH_ARMv5) {
-
idcode = s3c24xx_read_idcode_v5();
-
} else {
-
idcode = s3c24xx_read_idcode_v4();
-
}
-
-
arm_pm_restart = s3c24xx_pm_restart;
-
-
s3c_init_cpu(idcode, cpu_ids, ARRAY_SIZE(cpu_ids));
-
}
-
static unsigned long s3c24xx_read_idcode_v4(void)
-
{
-
#ifndef CONFIG_CPU_S3C2400
-
return __raw_readl(S3C2410_GSTATUS1);//S3C2410_GSTATUS1 读取chip ID
-
#else
-
return 0UL;
-
#endif
-
}
-
void __init s3c_init_cpu(unsigned long idcode,
-
struct cpu_table *cputab, unsigned int cputab_size)
-
{
-
cpu = s3c_lookup_cpu(idcode, cputab, cputab_size);
-
-
if (cpu == NULL) {
-
printk(KERN_ERR "Unknown CPU type 0x%08lx\n", idcode);
-
panic("Unknown S3C24XX CPU");
-
}
-
-
printk("CPU %s (id 0x%08lx)\n", cpu->name, idcode);
-
-
if (cpu->map_io == NULL || cpu->init == NULL) {
-
printk(KERN_ERR "CPU %s support not enabled\n", cpu->name);
-
panic("Unsupported Samsung CPU");
-
}
-
-
cpu->map_io();
-
}
-
static struct cpu_table * __init s3c_lookup_cpu(unsigned long idcode,
-
struct cpu_table *tab,
-
unsigned int count)
-
{
-
for (; count != 0; count--, tab++) {
-
if ((idcode & tab->idmask) == (tab->idcode & tab->idmask))
-
return tab;
-
}
-
-
return NULL;
-
}
由上述代码可值,通过比较芯片chip ID 和 tab->idcode,从cpu_table中找到正确的回调函数
接着就到了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");//clk_get从一个时钟list链表中以字符id名称来查找一个时钟clk结构体并且返回
-
xtal = clk_get_rate(xtal_clk);//返回 xtal_clk->rate
-
clk_put(xtal_clk);//module_put(clk->owner);?
-
fclk = s3c24xx_get_pll(__raw_readl(S3C2410_MPLLCON), xtal) * 2; //获取fclk频率
-
clkdiv = __raw_readl(S3C2410_CLKDIVN);//读取分频寄存器
-
camdiv = __raw_readl(S3C2440_CAMDIVN);
-
-
switch (clkdiv & S3C2440_CLKDIVN_HDIVN_MASK) {
-
case S3C2440_CLKDIVN_HDIVN_1://HCLK = FCLK/1.
-
hdiv = 1;
-
break;
-
case S3C2440_CLKDIVN_HDIVN_2://HCLK = FCLK/2
-
hdiv = 2;
-
break;
-
case S3C2440_CLKDIVN_HDIVN_4_8://HCLK = FCLK/4 when CAMDIVN[9] = 0 HCLK= FCLK/8 when CAMDIVN[9] = 1
-
hdiv = (camdiv & S3C2440_CAMDIVN_HCLK4_HALF) ? 8 : 4;
-
break;
-
case S3C2440_CLKDIVN_HDIVN_3_6://11 : HCLK = FCLK/3 when CAMDIVN[8] = 0 HCLK = FCLK/6 when CAMDIVN[8] = 1.
-
hdiv = (camdiv & S3C2440_CAMDIVN_HCLK3_HALF) ? 6 : 3;
-
break;
-
}
-
hclk = fclk / hdiv;//计算hclk
-
pclk = hclk / ((clkdiv & S3C2440_CLKDIVN_PDIVN) ? 2 : 1);//计算pclk
-
-
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);
-
}
-
static inline unsigned int
-
s3c24xx_get_pll(unsigned int pllval, unsigned int baseclk)
-
{
-
unsigned int mdiv, pdiv, sdiv;
-
uint64_t fvco;
-
-
mdiv = pllval >> S3C24XX_PLLCON_MDIVSHIFT;
-
pdiv = pllval >> S3C24XX_PLLCON_PDIVSHIFT;
-
sdiv = pllval >> S3C24XX_PLLCON_SDIVSHIFT;
//提取MPLLCON 寄存器相关值
-
mdiv &= S3C24XX_PLLCON_MDIVMASK;
-
pdiv &= S3C24XX_PLLCON_PDIVMASK;
-
sdiv &= S3C24XX_PLLCON_SDIVMASK;
-
-
fvco = (uint64_t)baseclk * (mdiv + 8);// mpll(fclk) = ( 2 *(mdiv + 8) * 12000000 )/((pdiv + 2) * (1<))
-
do_div(fvco, (pdiv + 2) << sdiv); //do_div(a,b) = a/b,做除法
-
-
return (unsigned int)fvco;
-
}
-
void __init_or_cpufreq s3c24xx_setup_clocks(unsigned long fclk,
-
unsigned long hclk,
-
unsigned long pclk)
-
{
-
clk_upll.rate = s3c24xx_get_pll(__raw_readl(S3C2410_UPLLCON),
-
clk_xtal.rate);
-
-
clk_mpll.rate = fclk;
-
clk_h.rate = hclk;
-
clk_p.rate = pclk;
-
clk_f.rate = fclk;
-
}
---------------------------------------------------------------------------
在这里有必要简单的说下s3c2440的时钟发生模块
linuxBSPmini2440之时钟
主时钟源来自外部晶振(XTIpll)或者外部时钟。用芯片引脚OM[3:2]来选择,一般情况下,OM3和OM2接地,这样,主时钟源即为外部晶振(此时USB时钟源也是外部晶振)。集成电路MPLL产生一个在频率和相位上同步于参考输入信号的输出信号,计算公式为:
mpll(fclk) = ( 2 *(mdiv + 8) * Fin)/((pdiv + 2) * (1<
图中的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);
//遍历clocks为链表头的实体链表,对实体链表中的数据结构进行处理,这是个for循环语句
list_for_each_entry(p, &clocks, list) {
//比较时钟的名字
if (p->id == idno &&
strcmp(id, p->name) == 0 &&
try_module_get(p->owner)) {
clk = p;//找到对应clk 结构
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时钟的注册和其他分开始因为它比较特别。