Linux啟動過程分析(十一)---da850_set_emif_clk_rate()函式分析
/*
* 雖然在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;
do {
v = __raw_readl(pll->base + PLLSTAT);
} while (v & PLLSTAT_GOSTAT);
v = __raw_readl(pll->base + clk->div_reg);
v &= ~pll->div_ratio_mask;
v |= ratio | PLLDIV_EN;
__raw_writel(v, pll->base + clk->div_reg);
v = __raw_readl(pll->base + PLLCMD);
v |= PLLCMD_GOSET;
__raw_writel(v, pll->base + PLLCMD);
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;
再往下主要是針對暫存器的一些賦值,需要對照手冊看。