内核时钟操作
Include/linuc/clk.h分析
首先打开sourceinsight内核工程,搜索clk.h,这个文件里的函数都是与平台无关,他们的实现都是不同的平台有不同的实现方法。换句话说操作clk的接口都是在这个接口里,这些函数的具体实现在不同的SOC芯片下是不同的。下面介绍的具体实现都是以S3C2440平台的
获取时钟频率,当内核移植到一个平台时,内核时钟驱动里就已经有了很多时钟structclk表(如S3C24XX),就像平台总线一样,然后用户的设备(如S3C2440的看门狗)安装时就会把这个设备的时钟设备名id如”watchdog”和内核驱动里的时钟数组表进行匹配,如果匹配失败就会返回错误码,这个错误码可以用IS_ERR()函数来判断
/**
*clk_get - lookup and obtain a reference to a clock producer.
*@dev: device for clock "consumer"
*@id: clock comsumer ID
*
*Returns a struct clk corresponding to the clock producer, or
*valid IS_ERR() condition containing errno. The implementation
*uses @dev and @id to determine the clock consumer, and thereby
*the clock producer.  (IOW, @id may beidentical strings, but
*clk_get may return different clock producers depending on @dev.)
*
*Drivers must assume that the clock source is not enabled.
*
*clk_get should not be called from within interrupt context.
*/
struct clk *clk_get(struct device *dev,const char *id); 
上面获取到了时钟结构,下面就可以使能他
int clk_enable(struct clk *clk);
/**
*clk_get_rate - obtain the current clock rate (in Hz) for a clock source.
*               This is only valid once the clock source has been enabled.
*@clk: clock source
*/
unsigned long clk_get_rate(struct clk*clk);
获取时钟频率
/**
*clk_get_parent - get the parent clock source for this clock
*@clk: clock source
*
*Returns struct clk corresponding to parent clock source, or
*valid IS_ERR() condition containing errno.
*/
struct clk *clk_get_parent(struct clk*clk);
获取父总线的时钟结构
下面介绍S3C2440平台的时钟结构,在内核移植到这个平台时就会注册这些时钟结构
首先打开sourceinsight内核工程,搜索clock-**.c,选取/arch/arm/plat-s3c24xx/clock-dclk.c,这个文件适合所有S3C24XX平台,再搜索clock.c,然后我们就可以选取   /arch/arm/mach-s3c2440/clock.c文件,这是S3C2440平台的时钟结构,不属于S3C24XX通用的时钟结构,还有\arch\arm\plat-s3c24xx\s3c2410.c(s3c2440也适用)
/* external clock definitions */
struct clk s3c24xx_dclk0 = {
	.name       = "dclk0",//和clk.h中的clk_get(struct device *dev,const char *id);里的//id进行匹配
	.id     = -1,
	.ctrlbit    = S3C2410_DCLKCON_DCLK0EN,
	// #define S3C2410_DCLKCON_DCLK1EN     (1<<16)他其实就是对时钟控制寄存器的位操作
	.enable         = s3c24xx_dclk_enable,
	.set_parent = s3c24xx_dclk_setparent,
	.set_rate   = s3c24xx_set_dclk_rate,
	.round_rate = s3c24xx_round_dclk_rate,
};
struct clk s3c24xx_dclk1 = {
	.name       = "dclk1",
	.id     = -1,
	.ctrlbit    = S3C2410_DCLKCON_DCLK1EN,
	.enable     = s3c24xx_dclk_enable,
	.set_parent = s3c24xx_dclk_setparent,
	.set_rate   = s3c24xx_set_dclk_rate,
	.round_rate = s3c24xx_round_dclk_rate,
};
struct clk s3c24xx_clkout0 = {
	.name       = "clkout0",
	.id     = -1,
	.set_parent = s3c24xx_clkout_setparent,
};
struct clk s3c24xx_clkout1 = {
	.name       = "clkout1",
	.id     = -1,
	.set_parent = s3c24xx_clkout_setparent,
};
/* Extra S3C2440 clocks */
static struct clk s3c2440_clk_cam = {
	.name                = "camif",
	.id              = -1,
	.enable              = s3c2410_clkcon_enable,  //这个平台实现的这个camif时钟模块使能函数
	.ctrlbit      = S3C2440_CLKCON_CAMERA,
};
static struct clk s3c2440_clk_cam_upll = {
	.name                = "camif-upll",
	.id              = -1,
	.set_rate = s3c2440_camif_upll_setrate,
	.round_rate     = s3c2440_camif_upll_round,
};
static struct clk s3c2440_clk_ac97 = {
	.name                = "ac97",
	.id              = -1,
	.enable              = s3c2410_clkcon_enable,
	.ctrlbit      = S3C2440_CLKCON_CAMERA,
};
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,
					},
};
下面介绍S3C2440平台的上面的函数的实现方法
首先打开sourceinsight内核工程,搜索clock-,然后我们就可以选取clock-dclk.c  /arch/arm/plat-s3c244xx/clock-dclk.c文件,这是S3C2440平台的时钟实现源文件
还有/arch/arm/plat-s3c244xx/s3c2410-clock.c文件
/* clocks that could be registered byexternal code */
static int s3c24xx_dclk_enable(struct clk *clk,int enable)
{
	unsignedlong dclkcon = __raw_readl(S3C24XX_DCLKCON);//读取时钟控制寄存器里的值
	if(enable)
		dclkcon|= clk->ctrlbit;//将这个时钟模块的时钟使能位打开使能
	else
		dclkcon&= ~clk->ctrlbit;
	__raw_writel(dclkcon,S3C24XX_DCLKCON);//再将时钟控制寄存器写回去
	return0;
}
代码举例:
例如我想获得S3C2440平台的看门狗时钟
在模块设备.c文件里添加
static struct clk *ldm_clk;
static int ldm_probe(static platform_device * pdev)
{
	ldm_clk= clk_get(pdev, “watchdog”);// id“watchdog”用来匹配内核里的时钟模块驱动里的时钟结构表里的id
	if(IS_ERR(ldm_clk))
		printk(KERN_ERR”clk_get failed\n”);
	ret=PTR_ERR(ldm_clk);//返回的错误码是个指针,我们要强转为int类型
	goto err_clk_get;
	ret=clk_enable(ldm_clk);
	if(ret<0)
	{
		printk(KERN_ERR” clk_enable \n”);
		goto clk_enable;
	}
	//看下我们的使能的看门狗时钟频率是多少
	printk(“clk_get_rate=%lu\n”, clk_get_rate(ldm_clk));
err_clk_get:
	err_clk_get:
}

小技巧:

       像struct clk *clk_get(struct device *dev, const char *con_id)函数返回值是一个结构体,当这个函数获取时钟失败时还是要返回一个指针,如何返回一个错误码呢?clk_get用ERR_PTR宏将错误码强转为一个指针,这样就和返回类型struct clk*兼容了,当你想要获得这个错误码时又可以通过宏PTR_ERR将这个错误码指针强制转换会错误码整形类型