Chinaunix首页 | 论坛 | 博客
  • 博客访问: 215263
  • 博文数量: 78
  • 博客积分: 3169
  • 博客等级: 中校
  • 技术积分: 805
  • 用 户 组: 普通用户
  • 注册时间: 2011-03-25 13:50
文章分类
文章存档

2012年(1)

2011年(77)

分类: LINUX

2011-04-19 14:47:36

 

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(...)中被调用 .
static void __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)
{
unsigned long 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的chip id查找到当前是的cpu_table结构.
cpu = s3c_lookup_cpu(idcode);
 

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 S3C24XX CPU");
}
 

// 赋值机器重启调用函数.
arm_pm_restart = s3c24xx_pm_restart;
 

// 调用当前cpu的IO映射函数.
(cpu->map_io)(mach_desc, size);
}
其中对于s3c2410芯片来说读到的ID为0x32410000,则对应的cpu_table结构为:
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
},
终于说到了这个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)
{
unsigned long tmp;
unsigned long fclk;
unsigned long hclk;
unsigned long 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.%03ld MHz\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,
unsigned long fclk,
unsigned long hclk,
unsigned long 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");
 

return 0;
}
注册了6个时钟, 所谓注册就是将这些时钟加入到了clocks的链表中.
时钟的结构如下:
 

struct clk {
// 属性
struct list_head list; // 链表头
struct module *owner; // 所属模块
struct clk *parent; // 父节点
const char *name; // 名称
int id; // ID
int usage; // 使用计数,当>0时,便 enable,<=0时,disable
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); // 设置父节点.
};
 

// 添加所有s3c2410使用的时钟.
// 在每一个用来初始化设备的调用没有实际完成前,我们不能使用这个系统设备 .
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的使能函数,在这个函数中通过写慢速时钟控制寄存器的来使能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");
 

return 0;
}
初始化后需要关闭的时钟:
 

/* standard clock definitions */
 

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

3 时钟的使用
比如在s3c2410的nand flash驱动的使用中
获取时钟: 在驱动加载时
// 获得给nand flash使用的时钟控制结构.并便能该时钟.
static int 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;
goto exit_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);即调用:
int s3c2410_clkcon_enable(struct clk *clk, int enable)
{
unsigned int clocks = clk->ctrlbit;
unsigned long 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);
 

return 0;
}
通过写时钟控制寄存器将nand时钟使能.同样也可以通过这个函数将时钟关闭.
同样在电源管理的suspend函数中将时钟关闭,在resume中将时钟打开.
 
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/dujie_abc/archive/2008/12/01/3418275.aspx
阅读(1133) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~