/*
* 虽然在bootloader中已经把emif的时钟速率设置为允许的值,但是内核需要重新
*设置以使它支持平台请求的特定时钟速率。
*/
ret = da850_set_emif_clk_rate()->
static __init int da850_set_emif_clk_rate(void)
{
struct clk *emif_clk;
emif_clk = clk_get(NULL, "pll0_sysclk3");
if (WARN(IS_ERR(emif_clk), "Unable to get emif clock\n"))
return PTR_ERR(emif_clk);
return clk_set_rate(emif_clk, CONFIG_DA850_FIX_PLL0_SYSCLK3RATE);
}
先来分析一下clk_get这个函数(位于drivers\clk\Clkdev.c):
:
struct clk *clk_get(struct device *dev, const char *con_id)
{
const char *dev_id = dev ? dev_name(dev) : NULL;
return clk_get_sys(dev_id, con_id);
}
因为dev传递下来的值是NULL,所以dev_id的值也是NULL;
然后继续调用clk_get_sys(NULL,“pll0_sysclk3”);
struct clk *clk_get_sys(const char *dev_id, const char *con_id)
{
struct clk_lookup *cl;
mutex_lock(&clocks_mutex);
cl = clk_find(dev_id, con_id);
if (cl && !__clk_get(cl->clk))
cl = NULL;
mutex_unlock(&clocks_mutex);
return cl ? cl->clk : ERR_PTR(-ENOENT);
}
clk_get_sys的实现依赖于clk_find这个函数
clk_find(NULL,“pll0_sysclk3”);
/*
* Find the correct struct clk for the device and connection ID.
* We do slightly fuzzy matching here:
* An entry with a NULL ID is assumed to be a wildcard.
* If an entry has a device ID, it must match
* If an entry has a connection ID, it must match
* Then we take the most specific entry - with the following
* order of precedence: dev+con > dev only > con only.
*/
static struct clk_lookup *clk_find(const char *dev_id, const char *con_id)
{
struct clk_lookup *p, *cl = NULL;
int match, best = 0;
list_for_each_entry(p, &clocks, node) {
match = 0;
if (p->dev_id) {
if (!dev_id || strcmp(p->dev_id, dev_id))
continue;
match += 2;
}
if (p->con_id) {
if (!con_id || strcmp(p->con_id, con_id))
continue;
match += 1;
}
if (match > best) {
cl = p;
if (match != 3)
best = match;
else
break;
}
}
return cl;
}
list_for_each_entry函数从clocks的链表中的表头,开始和我们传入的硬件相比较,如果找到了就返回一个指向该硬件clk_lookup类型的指针。
struct clk;
struct device;
struct clk_lookup {
struct list_head node;
const char *dev_id;
const char *con_id;
struct clk *clk;
};
clk_lookup中clk内容是:
static struct clk pll0_sysclk3 = {
.name = "pll0_sysclk3",
.parent = &pll0_clk,
.flags = CLK_PLL,
.div_reg = PLLDIV3,
.set_rate = da850_set_pll0sysclk3_rate,
.maxrate = 152000000,
};
紧接着来分析clk_set_rate(emif_clk, CONFIG_DA850_FIX_PLL0_SYSCLK3RATE)这个函数。
int clk_set_rate(struct clk *clk, unsigned long rate)
{
unsigned long flags;
int ret = -EINVAL;
if (clk == NULL || IS_ERR(clk))
return ret;
if (clk->set_rate)
ret = clk->set_rate(clk, rate);
spin_lock_irqsave(&clockfw_lock, flags);
if (ret == 0) {
if (clk->recalc)
clk->rate = clk->recalc(clk);
propagate_rate(clk);
}
spin_unlock_irqrestore(&clockfw_lock, flags);
return ret;
}
#define CONFIG_DA850_FIX_PLL0_SYSCLK3RATE 0
其中clk->set_rate指向的函数是:da850_set_pll0sysclk3_rate(struct clk *clk, unsigned long rate);
参数传递后如下:
da850_set_pll0sysclk3_rate(emif_clk,0)->
static int da850_set_pll0sysclk3_rate(struct clk *clk, unsigned long rate)
{
struct clk *arm_clk;
unsigned long sys_clk3_rate = 148000000;
int ret;
arm_clk = clk_get(NULL, "arm");
if (WARN(IS_ERR(arm_clk), "Unable to get ARM clock\n"))
return PTR_ERR(arm_clk);
/* Set EMIF clock based on OPPs */
switch (clk_get_rate(arm_clk)) {
case 200000000:
sys_clk3_rate = 75000000;
break;
case 96000000:
sys_clk3_rate = 50000000;
break;
}
if (rate)
sys_clk3_rate = min(sys_clk3_rate, rate);
ret = davinci_set_sysclk_rate(clk, sys_clk3_rate);
if (WARN_ON(ret))
return ret;
return 0;
}
这里重点分析arm_clk = clk_get(NULL, “arm”);
会返回指向arm的clk_lookup类型的指针,clk_lookup中的clk即arm_clk的内容是:
static struct clk arm_clk = {
.name = "arm",
.parent = &pll0_sysclk6,
.lpsc = DA8XX_LPSC0_ARM,
.flags = ALWAYS_ENABLED,
.set_rate = da850_set_armrate,
.round_rate = da850_round_armrate,
};
clk_get_rate(arm_clk)->
unsigned long clk_get_rate(struct clk *clk)
{
if (clk == NULL || IS_ERR(clk))
return -EINVAL;
return clk->rate;
}
这个函数会返回arm_clk结构体中存储的rate值。但目前看来这个值在结构体中没有定义,所以到
ret = davinci_set_sysclk_rate(clk, sys_clk3_rate)这个地方来执行
将参数传入:
ret = davinci_set_sysclk_rate(emif_clk, 148000000)->
int davinci_set_sysclk_rate(struct clk *clk, unsigned long rate)
{
unsigned v;
struct pll_data *pll;
unsigned long input;
unsigned ratio = 0;
/* If this is the PLL base clock, wrong function to call */
if (clk->pll_data)
return -EINVAL;
/* There must be a parent... */
if (WARN_ON(!clk->parent))
return -EINVAL;
/* ... the parent must be a PLL... */
if (WARN_ON(!clk->parent->pll_data))
return -EINVAL;
/* ... and this clock must have a divider. */
if (WARN_ON(!clk->div_reg))
return -EINVAL;
pll = clk->parent->pll_data;
input = clk->parent->rate;
/* If pre-PLL, source clock is before the multiplier and divider(s) */
if (clk->flags & PRE_PLL)
input = pll->input_rate;
if (input > rate) {
/*
* Can afford to provide an output little higher than requested
* only if maximum rate supported by hardware on this sysclk
* is known.
*/
if (clk->maxrate) {
ratio = DIV_ROUND_CLOSEST(input, rate);
if (input / ratio > clk->maxrate)
ratio = 0;
}
if (ratio == 0)
ratio = DIV_ROUND_UP(input, rate);
ratio--;
}
if (ratio > pll->div_ratio_mask)
return -EINVAL;
/*等待PLLSTAT位被清零(表示当前没有操作)*/
do {
v = __raw_readl(pll->base + PLLSTAT);
} while (v & PLLSTAT_GOSTAT);
/*这里的clk指的是pll0_sysclk3,则div_reg = PLLDIV3= 0x120,对应的寄存器为PLLDIV3 PLLC0 Divider 3 Register*/
v = __raw_readl(pll->base + clk->div_reg);
/*先将对应的掩码清零*/
v &= ~pll->div_ratio_mask;
/*指定分频系数的同时将Divider3使能*/
v |= ratio | PLLDIV_EN;
__raw_writel(v, pll->base + clk->div_reg);
/*设置PLLCMD的GOSET位为1,来传输新的分频参数*/
v = __raw_readl(pll->base + PLLCMD);
v |= PLLCMD_GOSET;
__raw_writel(v, pll->base + PLLCMD);
/*等待PLLSTAT的值被清零,表示分频的结束*/
do {
v = __raw_readl(pll->base + PLLSTAT);
} while (v & PLLSTAT_GOSTAT);
return 0;
}
emif_clk所对应的clk结构体如下:
static struct clk pll0_sysclk3 = {
.name = "pll0_sysclk3",
.parent = &pll0_clk,
.flags = CLK_PLL,
.div_reg = PLLDIV3,
.set_rate = da850_set_pll0sysclk3_rate,
.maxrate = 152000000,
};
前面的几个if经过判断,都没有错误,接下来看:
pll = clk->parent->pll_data;
展开:
pll=pll0_clk->pll_data;
pll=pll0_data;
->
static struct pll_data pll0_data = {
.num = 1,
.phys_base = DA8XX_PLL0_BASE,
.flags = PLL_HAS_PREDIV | PLL_HAS_POSTDIV,
};
#define DA8XX_PLL0_BASE 0x01c11000
#define PLL_HAS_PREDIV 0x01
#define PLL_HAS_POSTDIV 0x02
input = clk->parent->rate;
input=pll0_clk->rate=0;
接下来继续看clk_set_rate的后半部分:
if (ret == 0) {
if (clk->recalc)
clk->rate = clk->recalc(clk);
propagate_rate(clk);
}
没有执行。