一、相关概念:
SYSCLK:系统时钟。它有两个来源,OSC_CLK(主振荡器时钟)和RTC_CLK(RTC时钟)。选择那个时钟源取决于SYSCLK_CTRL的控制。
ARM_CLK:在SYSCLK后有一个HCLKPLL用于将SYSCLK转换成ARM_CLK。ARM_CLK用于ARMCPU,MSSDCLK。在寄存器HCLK_CTRL中,ARM_CLK将会被分频成HCLK,PCLK和DDRAM_CLK。
HCLK:AHB总线时钟。用于AHB矩阵和USBAHB、AHB从机、FAB从机和APB从机
P_CLK:外设时钟。
CLK48MHz:USB48MHz时钟——基于OSC_CLK。
DDRAM_CLK:DDR SDRAM时钟
MSSDCLK:SD卡时钟。
二、clock.c中有关函数介绍
clk结构体定义
struct clk {
structlist_head node;
struct module*owner;
const char*name; /* Clock name */
struct clk*parent; /* Parent clock */
u32 rate; /* Rate in Hz for the clock (0= disabled) */
u32 flags; /* Setup flags */
s8 usecount; /* Number of users of thisclock */
/* Requiredfunctions per clock */
int(*set_rate) (struct clk *, u32);
u32(*round_rate) (struct clk *, u32);
int (*enable)(struct clk *clk, int);
/* Optionalfunctions per clock */
int(*set_parent) (struct clk * clk, struct clk * parent);
u32(*get_rate) (struct clk *clk);
/* Registerand mask for enablings/disabling the clock, only
used when the CLK_FLAG_ST_ENAB flag is set*/
u32enable_reg; /* Register toenable and disable associated clock */
u32enable_mask; /* Or mask forenable, AND ~mask for disable */
};
static void clk_upate_children(struct clk *clk)
{
local_update_armpll_rate();
local_update_usbpll_rate();
}
这个函数作用就是根据输入的系统时钟SYSCLK和相关寄存器的设置来获得clk_armpll.rate和clk_usbpll.rate 的成员值。当然后面还有函数会根据这个值去设置相关寄存器。
local_update_armpll_rate()和local_update_usbpll_rate()实现过程相仿。只追踪第一个函数的实现过程。
static void local_update_armpll_rate(void)
{
u32 clkin,pllreg;
/* Get PLLinput clock rate */
获得系统时钟速率
clkin =clk_armpll.parent->rate;
/* Get ARMHCLKPLL register */
pllreg =__raw_readl(CLKPWR_HCLKPLL_CTRL(CLKPWR_IOBASE)) & 0x1FFFF;
根据系统时钟速度,和一个配置项获得ARM_CLK的速率。
clk_armpll.rate= local_clk_get_pllrate_from_reg(clkin, pllreg);
}
static u32 local_clk_find_pll_cfg(u32 pllin_freq,
u32 target_freq,
CLKPWR_HCLK_PLL_SETUP_T *pllsetup)
这个函数作用是根据目前的pllin_freq和target_freq为设定pllsetup 。即如果按照pllsetup的项去配置相关寄存器,即可实现由pllin_freq 到target_freq的目标。而函数
static u32local_clk_hclkpll_setup(CLKPWR_HCLK_PLL_SETUP_T *pHCLKPllSetup)就是做这个事情的。
static int local_armpll_set_rate(struct clk *clk, u32rate)封装上面介绍的两个函数。这函数很简单,给定一个时钟,然后再给定一个要设置的速率。就能实现目标。不看其具体的实现就知道,它先要根据rate和clk->parent.rate为自己找到一个合适的pllsetup,然后再将任务传给local_clk_hclkpll_setup()去实现。
static struct clk *chip_clks[] = {
&osc_32KHz,
&osc_pll397,
&osc_main,
&clk_sys,
&clk_armpll,
&clk_hclk,
&clk_pclk,
&clk_usbpll,
&clk_timer0,
&clk_timer1,
&clk_timer2,
&clk_timer3,
&clk_vfp9,
&clk_dma,
#if defined (CONFIG_LPC32XX_WATCHDOG)
&clk_wdt,
#endif
#if defined (CONFIG_MACH_LPC32XX_UART3_ENABLE)
&clk_uart3,
#endif
#if defined (CONFIG_MACH_LPC32XX_UART4_ENABLE)
&clk_uart4,
#endif
#if defined (CONFIG_MACH_LPC32XX_UART5_ENABLE)
&clk_uart5,
#endif
#if defined (CONFIG_MACH_LPC32XX_UART6_ENABLE)
&clk_uart6,
#endif
#if defined (CONFIG_MACH_LPC32XX_I2C0_ENABLE)
&clk_i2c0,
#endif
#if defined (CONFIG_MACH_LPC32XX_I2C1_ENABLE)
&clk_i2c1,
#endif
#if defined (CONFIG_MACH_LPC32XX_USBOTG_I2C_ENABLE)
&clk_i2c2,
#endif
#if defined(CONFIG_SPI_LPC32XX)
#if defined(CONFIG_MACH_LPC32XX_SSP0_ENABLE)
&clk_ssp0,
#endif
#if defined(CONFIG_MACH_LPC32XX_SSP1_ENABLE)
&clk_ssp1,
#endif
#endif
#if defined(CONFIG_KEYBOARD_LPC32XX)
&clk_kscan,
#endif
#if defined(CONFIG_MTD_NAND_SLC_LPC32XX)
&clk_nand,
#endif
#if defined(CONFIG_SND_LPC3XXX_SOC_I2S)
&clk_i2s0,
&clk_i2s1,
#endif
#if defined (CONFIG_TOUCHSCREEN_LPC32XX)
&clk_tsc,
#endif
#if defined (CONFIG_MMC_ARMMMCI)
&clk_mmc,
#endif
#if defined (CONFIG_LPC32XX_MII)
&clk_net,
#endif
#if defined (CONFIG_FB_ARMCLCD)
&clk_lcd,
#endif
#if defined (CONFIG_USB_GADGET_LPC32XX)
&clk_usbd,
#endif
};
这个结构体列出了lpc3250可能用到的所有时钟,而时钟的具体实现就是利用前面介绍的函数。
struct clk *local_clk_get(struct device *dev, const char*id)这个函数一般是会在id的位置传入一个时钟名称。然后该函数返回这个时钟的结构体。第一个参数没什么用处。
static int local_clk_enable(struct clk *clk)这个函数只是根据具体的时钟,来调用clk->enable成员方法。
struct clk *clk_get(struct device *dev, const char *id)是对struct clk *local_clk_get()函数的封装。只是中间添加了这么一项:在获取时钟结构体期间禁能了中断
int clk_is_sysclk_mainosc(void)判断系统时钟是否是从OSC_CLK(主振荡器时钟)获取。
最后的int __initclk_init(void)函数,分别注册了lpc3250的时钟,确定了系统时钟源。增加引用计数。虽然这个函数将时钟注册进了linux系统,但是这配置都还是默认的配置。ARM_CLK和OSC_CLK还是一样的。所以时钟在具体使用的时候,还要再具体配置。