内核时钟操作
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将这个错误码指针强制转换会错误码整形类型