S3C2410的时钟驱动分析
来源:互联网 发布:网络上下其手的意思 编辑:程序博客网 时间:2024/05/22 08:06
S3C2410的时钟驱动分析
1起点
我们以QT2410这个机器配置为例来说明.
在文件.../arch/arm/mach-s3c2410/mach-qt2410.c中我们定义一个机器配置
MACHINE_START(QT2410,"QT2410")
.phys_io=S3C2410_PA_UART,
.io_pg_offst=(((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
.boot_params=S3C2410_SDRAM_PA + 0x100,
.map_io=qt2410_map_io,
.init_irq=s3c24xx_init_irq,
.init_machine=qt2410_machine_init,
.timer=&s3c24xx_timer,
MACHINE_END
当内核启动时以QT2410的配置启动时,在start_kernel(...)中会调用map_io()即qt2410_map_io()函数.
//在start_kernel(...)中被调用.
staticvoid __init qt2410_map_io(void)
{
//添加IO地址映射.
s3c24xx_init_io(qt2410_iodesc,ARRAY_SIZE(qt2410_iodesc));
//初始化各种时钟源.
s3c24xx_init_clocks(12*1000*1000);
//初始化UART
s3c24xx_init_uarts(smdk2410_uartcfgs,ARRAY_SIZE(smdk2410_uartcfgs));
}
在说s3c24xx_init_clocks(12*1000*1000);之前我们需要先看一下前面那个函数.
其中在s3c24xx_init_io(...)中
void__init s3c24xx_init_io(struct map_desc *mach_desc, int size)
{
unsignedlong idcode = 0x0;
/*initialise the io descriptors we need for initialisation */
//初始化我们需要的IO描述符,
iotable_init(s3c_iodesc,ARRAY_SIZE(s3c_iodesc));
//读CPU的结构代码,当前为0x32410000
if(cpu_architecture() >= CPU_ARCH_ARMv5) {
idcode= s3c24xx_read_idcode_v5();
}else {
idcode= s3c24xx_read_idcode_v4();
}
//根据当前CPU的chipid查找到当前是的cpu_table结构.
cpu= s3c_lookup_cpu(idcode);
if(cpu == NULL) {
printk(KERN_ERR"Unknown CPU type 0x%08lx/n", idcode);
panic("UnknownS3C24XX 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("UnsupportedS3C24XX CPU");
}
//赋值机器重启调用函数.
arm_pm_restart= s3c24xx_pm_restart;
//调用当前cpu的IO映射函数.
(cpu->map_io)(mach_desc,size);
}
其中对于s3c2410芯片来说读到的ID为0x32410000,则对应的cpu_table结构为:
staticstruct 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
},
终于说到了这个cpu_table结构,这个结构在s3c24xx_init_clocks(12*1000*1000)中会用到
2初始化
.../arch/arm/plat-s3c24xx/cpu.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);//初始化各种时钟源。
}
这里的(cpu->init_clocks)(xtal)实际为:
s3c2410_init_clocks(12* 1000 * 1000 )
.../arch/arm/mach-s3c2410/s3c2410.c
void__init s3c2410_init_clocks(int xtal)
{
unsignedlong tmp;
unsignedlong fclk;
unsignedlong hclk;
unsignedlong pclk;
/*now we've got our machine bits initialised, work out what
* clocks we've got */
//读取时钟控制寄存器内容,
fclk= s3c2410_get_pll(__raw_readl(S3C2410_MPLLCON), xtal);
//读取时钟分频寄存器内容
tmp= __raw_readl(S3C2410_CLKDIVN);
/*work out clock scalings */
//根据核心时钟计算/内存时钟/外围时钟.
hclk= fclk / ((tmp & S3C2410_CLKDIVN_HDIVN) ? 2 : 1);
pclk= hclk / ((tmp & S3C2410_CLKDIVN_PDIVN) ? 2 : 1);
/*print brieft summary of clocks, etc */
printk("S3C2410:core %ld.%03ld MHz, memory %ld.%03ld MHz, peripheral %ld.%03ldMHz/n",
print_mhz(fclk), print_mhz(hclk), print_mhz(pclk));
/*initialise the clocks here, to allow other things like the
* console to use them
*/
//初始化时钟源,让其它设备如控制台等可以使用它.
s3c24xx_setup_clocks(xtal,fclk, hclk, pclk);
//注册所有初始化的,未初始化的时钟.
s3c2410_baseclk_add();
}
/*initalise all the clocks */
//初始化所有时钟.
//xtal: 基础时钟,
//fclk:内核时钟
//hclk: 内存时钟
//pclk: 外围
int__init s3c24xx_setup_clocks(unsigned long xtal,
unsignedlong fclk,
unsignedlong hclk,
unsignedlong pclk)
{
printk(KERN_INFO"S3C24XX Clocks, (c) 2004 Simtec Electronics/n");
/*initialise the main system clocks */
//初始化主系统时钟
clk_xtal.rate= xtal;
clk_upll.rate= s3c2410_get_pll(__raw_readl(S3C2410_UPLLCON), xtal);
clk_mpll.rate= fclk;
clk_h.rate= hclk;
clk_p.rate= pclk;
clk_f.rate= fclk;
/*assume uart clocks are correctly setup */
/*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)
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");
return0;
}
注册了6个时钟,所谓注册就是将这些时钟加入到了clocks的链表中.
时钟的结构如下:
structclk {
//属性
structlist_head list;// 链表头
structmodule *owner;// 所属模块
structclk *parent;// 父节点
constchar *name;// 名称
int id;// ID
int usage;// 使用计数,当>0时,便enable,<=0时,disable
unsignedlong rate;// 速率
unsignedlong ctrlbit;// 控制位.
//操作函数
int (*enable)(struct clk *, int enable);// 使能
int (*set_rate)(struct clk *c, unsigned long rate);// 设置速率
unsignedlong (*get_rate)(struct clk *c);// 读取速率
unsignedlong (*round_rate)(struct clk *c, unsigned long rate);//
int (*set_parent)(struct clk *c, struct clk *parent);// 设置父节点.
};
//添加所有s3c2410使用的时钟.
//在每一个用来初始化设备的调用没有实际完成前,我们不能使用这个系统设备.
int__init s3c2410_baseclk_add(void)
{
//读慢时钟控制寄存器内容
unsignedlong clkslow = __raw_readl(S3C2410_CLKSLOW);
//读时钟控制寄存器内容.
unsignedlong clkcon = __raw_readl(S3C2410_CLKCON);
structclk *clkp;
structclk *xtal;
intret;
intptr;
//设置时钟clk_upll的使能函数,在这个函数中通过写慢速时钟控制寄存器的来使能UCLK
clk_upll.enable= s3c2410_upll_enable;
//注册 USB-BUS时钟,
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);
}
}
//我们必须小心去disable那些我们在启动时不想打开使用的时钟.例如,LCD子系统,他们向总线发出DMA请求可以导致系统去查询
//disableLCD时钟在LCD正在使用的时候是危险的。所以bootloader应该十分小心,如果不需要打开LCD,就不要儾使能它。
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");
return0;
}
初始化后需要关闭的时钟:
/*standard clock definitions */
staticstruct 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,
}
};
初始化后需要根据控制寄存器的状态来判断是否工作的时钟:
staticstruct 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,
},
};
3时钟的使用
比如在s3c2410的nandflash驱动的使用中
获取时钟:在驱动加载时
//获得给nandflash使用的时钟控制结构.并便能该时钟.
staticint s3c24xx_nand_probe(struct platform_device *pdev,
enum s3c_cpu_type cpu_type)
{
..............
info->clk= clk_get(&pdev->dev, "nand");
if(IS_ERR(info->clk)) {
dev_err(&pdev->dev,"failed to get clock/n");
err= -ENOENT;
gotoexit_error;
}
clk_enable(info->clk);
................
}
首先调用clk_get(...)通过比较名称”nand”找到时钟结构:
{
.name="nand",
.id=-1,
.parent=&clk_h,
.enable=s3c2410_clkcon_enable,
.ctrlbit=S3C2410_CLKCON_NAND,
},
然后调用clk_enable(info->clk);即调用:
ints3c2410_clkcon_enable(struct clk *clk, int enable)
{
unsignedint clocks = clk->ctrlbit;
unsignedlong clkcon;
clkcon= __raw_readl(S3C2410_CLKCON);
if(enable)
clkcon|= clocks;
else
clkcon&= ~clocks;
/*ensure none of the special function bits set */
clkcon&= ~(S3C2410_CLKCON_IDLE|S3C2410_CLKCON_POWER);
__raw_writel(clkcon,S3C2410_CLKCON);
return0;
}
通过写时钟控制寄存器将nand时钟使能.同样也可以通过这个函数将时钟关闭.
同样在电源管理的suspend函数中将时钟关闭,在resume中将时钟打开.
- S3C2410的时钟驱动分析
- S3C2410的RTC驱动分析
- S3C2410驱动分析的LCD驱动
- s3c2410的nand flash的驱动分析
- s3c2410的nand flash的驱动分析
- s3c2410的nand flash的驱动分析
- s3c2410 LCD驱动的结构分析
- S3C2410看门狗驱动分析
- S3C2410看门狗驱动分析 .
- S3C2410看门狗驱动分析
- s3c2410的时钟与电源
- s3c2410实时时钟模块要点分析
- s3c2410实时时钟模块要点分析
- 基于s3c2410的YL-LCD35液晶驱动的分析
- 基于S3C2410的SD卡linux驱动分析一
- 基于S3C2410的SD卡linux驱动分析二
- S3C2410时钟
- S3C2410驱动分析之ADC通用驱动
- PostQuitMessage
- SAP所有模块用户出口(User Exits) 四
- 用gdb观察函数调用过程中栈上的那些事儿
- 给25岁之前男人的忠告!
- DOS命令大全
- S3C2410的时钟驱动分析
- Asp.net中Frameset的使用小结
- 有关Eclipse 的资源
- Visual Studio 2008 每日提示(一)
- 转帖: Cookie和会话状态的工作原理及Cookie欺骗
- Head First C# 中文版 图文皆译 第三章 page88
- 从UIView里面截图 - Obtain UIImage From UIView
- 开博的首言
- php 投票模块 2