1. 程式人生 > >Linux啟動過程分析(十一)---da850_set_emif_clk_rate()函式分析

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;

再往下主要是針對暫存器的一些賦值,需要對照手冊看。