概述
这部分内容从Board-da850-sdi.c (archarmmach-davinci)开始:
MACHINE_START(DAVINCI_DA850_SDI, "DA850 SDI Development Board")
.atag_offset = 0x100,
.map_io = da850_evm_map_io,
.init_irq = cp_intc_init,
.timer = &davinci_timer,
.init_machine = da850_evm_init,
.dma_zone_size = SZ_128M,
.restart = da8xx_restart,
MACHINE_END
->
static void __init da850_evm_map_io(void)
{
da850_init();
}
->
在这里插入代码片
void __init da850_init(void)
{
unsigned int v;
davinci_common_init(&davinci_soc_info_da850);
da8xx_syscfg0_base = ioremap(DA8XX_SYSCFG0_BASE, SZ_4K);
if (WARN(!da8xx_syscfg0_base, "Unable to map syscfg0 module"))
return;
da8xx_syscfg1_base = ioremap(DA8XX_SYSCFG1_BASE, SZ_4K);
if (WARN(!da8xx_syscfg1_base, "Unable to map syscfg1 module"))
return;
/*
* Move the clock source of Async3 domain to PLL1 SYSCLK2.
* This helps keeping the peripherals on this domain insulated
* from CPU frequency changes caused by DVFS. The firmware sets
* both PLL0 and PLL1 to the same frequency so, there should not
* be any noticeable change even in non-DVFS use cases.
*/
da850_set_async3_src(1);
/* Unlock writing to PLL0 registers */
v = __raw_readl(DA8XX_SYSCFG0_VIRT(DA8XX_CFGCHIP0_REG));
v &= ~CFGCHIP0_PLL_MASTER_LOCK;
__raw_writel(v, DA8XX_SYSCFG0_VIRT(DA8XX_CFGCHIP0_REG));
/* Unlock writing to PLL1 registers */
v = __raw_readl(DA8XX_SYSCFG0_VIRT(DA8XX_CFGCHIP3_REG));
v &= ~CFGCHIP3_PLL1_MASTER_LOCK;
__raw_writel(v, DA8XX_SYSCFG0_VIRT(DA8XX_CFGCHIP3_REG));
}
先看davinci_common_init这个函数,因为我们关注的是和时钟相关的,所以进入其中的
davinci_clk_init(davinci_soc_info.cpu_clks)这个函数。
davici_soc_info这个结构体的定义在Da850.c (archarmmach-davinci)中,其中我们关注的是这一行:
.cpu_clks = da850_clks,
再对da850_clks进行追踪,内容如下:
static struct clk_lookup da850_clks[] = {
CLK(NULL, "ref", &ref_clk),
CLK(NULL, "pll0", &pll0_clk),
CLK(NULL, "pll0_aux", &pll0_aux_clk),
CLK(NULL, "pll0_sysclk1", &pll0_sysclk1),
CLK(NULL, "pll0_sysclk2", &pll0_sysclk2),
CLK(NULL, "pll0_sysclk3", &pll0_sysclk3),
CLK(NULL, "pll0_sysclk4", &pll0_sysclk4),
CLK(NULL, "pll0_sysclk5", &pll0_sysclk5),
CLK(NULL, "pll0_sysclk6", &pll0_sysclk6),
CLK(NULL, "pll0_sysclk7", &pll0_sysclk7),
CLK(NULL, "pll1", &pll1_clk),
CLK(NULL, "pll1_aux", &pll1_aux_clk),
CLK(NULL, "pll1_sysclk2", &pll1_sysclk2),
CLK(NULL, "pll1_sysclk3", &pll1_sysclk3),
CLK("i2c_davinci.1", NULL, &i2c0_clk),
CLK(NULL, "timer0", &timerp64_0_clk),
CLK("watchdog", NULL, &timerp64_1_clk),
CLK(NULL, "timer2", &timerp64_2_clk),
CLK(NULL, "timer3", &timerp64_3_clk),
CLK(NULL, "dsp", &dsp_clk),
CLK(NULL, "arm_rom", &arm_rom_clk),
CLK(NULL, "tpcc0", &tpcc0_clk),
CLK(NULL, "tptc0", &tptc0_clk),
CLK(NULL, "tptc1", &tptc1_clk),
CLK(NULL, "tpcc1", &tpcc1_clk),
CLK(NULL, "tptc2", &tptc2_clk),
CLK(NULL, "pruss", &pruss_clk),
CLK(NULL, "uart0", &uart0_clk),
CLK(NULL, "uart1", &uart1_clk),
CLK(NULL, "uart2", &uart2_clk),
CLK(NULL, "aintc", &aintc_clk),
CLK(NULL, "gpio", &gpio_clk),
CLK("i2c_davinci.2", NULL, &i2c1_clk),
CLK(NULL, "emif3", &emif3_clk),
CLK(NULL, "arm", &arm_clk),
CLK(NULL, "rmii", &rmii_clk),
CLK("davinci_emac.1", NULL, &emac_clk),
CLK("davinci-mcasp.0", NULL, &mcasp_clk),
CLK("da8xx_lcdc.0", NULL, &lcdc_clk),
CLK("davinci_mmc.0", NULL, &mmcsd0_clk),
CLK("davinci_mmc.1", NULL, &mmcsd1_clk),
CLK("davinci-mcbsp.1", NULL, &mcbsp1_clk),
CLK(NULL, "aemif", &aemif_clk),
CLK(NULL, "usb11", &usb11_clk),
CLK(NULL, "usb20", &usb20_clk),
CLK("spi_davinci.0", NULL, &spi0_clk),
CLK("spi_davinci.1", NULL, &spi1_clk),
CLK(NULL, "vpif", &vpif_clk),
CLK("ahci", NULL, &sata_clk),
CLK(NULL, "ehrpwm", &ehrpwm_clk),
CLK(NULL, "ecap", &ecap_clk),
CLK(NULL, NULL, NULL),
};
了解完了传入到davinci_clk_init中的参数,再来分析这个函数里面的具体操作:
int __init davinci_clk_init(struct clk_lookup *clocks)
{
struct clk_lookup *c;
struct clk *clk;
size_t num_clocks = 0;
/*依次遍历da850_clks数组中的每一项*/
for (c = clocks; c->clk; c++) {
clk = c->clk;//(1)
if (!clk->recalc) {
/* Check if clock is a PLL */
if (clk->pll_data)
clk->recalc = clk_pllclk_recalc;
/* Else, if it is a PLL-derived clock */
else if (clk->flags & CLK_PLL)
clk->recalc = clk_sysclk_recalc;
/* Otherwise, it is a leaf clock (PSC clock) */
else if (clk->parent)
clk->recalc = clk_leafclk_recalc;
}
if (clk->pll_data) {
struct pll_data *pll = clk->pll_data;
if (!pll->div_ratio_mask)
pll->div_ratio_mask = PLLDIV_RATIO_MASK;
if (pll->phys_base && !pll->base) {
pll->base = ioremap(pll->phys_base, SZ_4K);
WARN_ON(!pll->base);
}
}
if (clk->recalc)
clk->rate = clk->recalc(clk);
if (clk->lpsc)
clk->flags |= CLK_PSC;
clk_register(clk);
num_clocks++;
/* Turn on clocks that Linux doesn't otherwise manage */
if (clk->flags & ALWAYS_ENABLED)
clk_enable(clk);
}
clkdev_add_table(clocks, num_clocks);
return 0;
}
(1)c的类型是clk_lookup的结构体,这个结构体定义的形式如下:
CLK(NULL, “ref”, &ref_clk),
CLK的定义可以在Clock.h (archarmmach-davinci)中找到:
#define CLK(dev, con, ck)
{
.dev_id = dev,
.con_id = con,
.clk = ck,
}
由此可以得出:类似CLK(NULL, “ref”, &ref_clk)这样的定义形式,
c->clk指向的就是ref_clk;
而ref_clk也是一个结构体,它的内容如下:
static struct clk ref_clk = {
.name = "ref_clk",
.rate = DA850_REF_FREQ,
.set_rate = davinci_simple_set_rate,
};
最后,将c->clk的值指向了clk,
经过分析,实际最终执行的是:
clk_register(clk)
->
if (clk->rate)
return 0;
所以相当于设置了一个DA850_REF_FREQ=24000000的参考时钟
再来分析几个其它的pll时钟设置
static struct clk pll0_clk = {
.name = "pll0",
.parent = &ref_clk,
.pll_data = &pll0_data,
.flags = CLK_PLL,
.set_rate = da850_set_pll0rate,
};
在davinci_clk_init中找到对应的执行函数,因为pll0_clk中没有recalc的定义,所以(if!recalc)成立;
if (!clk->recalc) {
/* Check if clock is a PLL */
/*因为pll0_clk中有pll_data的定义,所以执行第一个if下面的语句*/
if (clk->pll_data)
/*
clk->recalc = clk_pllclk_recalc;
/* Else, if it is a PLL-derived clock */
else if (clk->flags & CLK_PLL)
clk->recalc = clk_sysclk_recalc;
/* Otherwise, it is a leaf clock (PSC clock) */
else if (clk->parent)
clk->recalc = clk_leafclk_recalc;
}
clk->recalc = clk_pllclk_recalc;
继续分析 davinci_clk_init:
/*&pll0_data,*/
if (clk->pll_data) {
/*pll=pll0_data*/
struct pll_data *pll = clk->pll_data;
/*pll0_data中没有对div_ratio_mask的定义,所以会往下执行*/
if (!pll->div_ratio_mask)
/*pll->div_ratio_mask= PLLDIV_RATIO_MASK= 0x1f*/
pll->div_ratio_mask = PLLDIV_RATIO_MASK;
/*pll->phys_base在这里指向phys_base = DA8XX_PLL0_BASE而且pll0_data中的pll->base
确实没有定义*/
if (pll->phys_base && !pll->base) {
/*io_remap用来将 DA8XX_PLL0_BASE这个地址空间映射到内核的虚拟地址空间上去,便于访问*/
pll->base = ioremap(pll->phys_base, SZ_4K);
WARN_ON(!pll->base);
}
}
接下来还有几个函数需要分析
if (clk->recalc)
clk->rate = clk->recalc(clk);
if (clk->lpsc)
clk->flags |= CLK_PSC;
在上面的分析中已经将 clk_pllclk_recalc赋值给clk->recalc ;
所以这里执行的就是,
那么实际上这个函数就是
clk->rate=clk_pllclk_recalc(pll0_clk)
下面再来分析一下clk_pllclk_recalc(pll0_clk)这个函数:
static unsigned long clk_pllclk_recalc(struct clk *clk)
{
u32 ctrl, mult = 1, prediv = 1, postdiv = 1;
u8 bypass;
/*struct pll_data *pll=&pll0_data*/
struct pll_data *pll = clk->pll_data;
unsigned long rate = clk->rate;
/*
static struct pll_data pll0_data = {
.num = 1,
.phys_base = DA8XX_PLL0_BASE,
.flags = PLL_HAS_PREDIV | PLL_HAS_POSTDIV,
}
实际上读出的是PLLC0 Control Register的配置
*/
ctrl = __raw_readl(pll->base + PLLCTL);
/*rate=pll->input_rate=ref-clk->rate=DA850_REF_FREQ=24000 000*/
rate = pll->input_rate = clk->parent->rate;
/*判断PLLC0 Control Register的第0位PLLEN到底配置为0还是1,如果是0的话,PLL0在bypass模式,如果
是1的话,PLL0模式被使能,而不是bypass模式*/
if (ctrl & PLLCTL_PLLEN) {
/*;这样的话!bypass=1,而使
rate /= prediv;
rate *= mult;
rate /= postdiv;
得以执行,从而使设置的倍频,预分频和后分频生效
*/
bypass = 0
/读出PLLC0 PLL Multiplier Control Register的配置*/
mult = __raw_readl(pll->base + PLLM);
if (cpu_is_davinci_dm365())
mult = 2 * (mult & PLLM_PLLM_MASK);
else
/*PLL multiplier select. Multiplier Value = PLLM + 1.*/
mult = (mult & PLLM_PLLM_MASK) + 1;
} else
bypass = 1;
/*根据PLLC0 Pre-Divider Control Register (PREDIV)的配置,进行预分频*/
if (pll->flags & PLL_HAS_PREDIV) {
prediv = __raw_readl(pll->base + PREDIV);
if (prediv & PLLDIV_EN)
prediv = (prediv & pll->div_ratio_mask) + 1;
else
prediv = 1;
}
/* pre-divider is fixed, but (some?) chips won't report that */
if (cpu_is_davinci_dm355() && pll->num == 1)
prediv = 8;
/*根据PLLC0 PLL Post-Divider Control Register的配置,进行后分频*/
if (pll->flags & PLL_HAS_POSTDIV) {
postdiv = __raw_readl(pll->base + POSTDIV);
if (postdiv & PLLDIV_EN)
postdiv = (postdiv & pll->div_ratio_mask) + 1;
else
postdiv = 1;
}
/*根据预分频,倍频,后分频的值,对频率进行改变*/
if (!bypass) {
rate /= prediv;
rate *= mult;
rate /= postdiv;
}
pr_debug("PLL%d: input = %lu MHz [ ",
pll->num, clk->parent->rate / 1000000);
if (bypass)
pr_debug("bypass ");
if (prediv > 1)
pr_debug("/ %d ", prediv);
if (mult > 1)
pr_debug("* %d ", mult);
if (postdiv > 1)
pr_debug("/ %d ", postdiv);
pr_debug("] --> %lu MHz output.n", rate / 1000000);
return rate;
}
PLLC0 Control Register等寄存器根据uboot.ais中的ROM进行初始化,参照https://blog.csdn.net/qq_40788950/article/details/85725898,PLL0的预分频为1,倍频为19,后分频为1。
得到的rate为456MHz。
static struct clk pll0_aux_clk = {
.name = "pll0_aux_clk",
.parent = &pll0_clk,
.flags = CLK_PLL | PRE_PLL,
};
它满足:
/* Else, if it is a PLL-derived clock */
else if (clk->flags & CLK_PLL)
clk->recalc = clk_sysclk_recalc;
接着执行:
if (clk->recalc)
clk->rate = clk->recalc(clk);
即clk->rate =clk_sysclk_recalc(pll0_aux_clk);
static unsigned long clk_sysclk_recalc(struct clk *clk)
{
u32 v, plldiv;
struct pll_data *pll;
unsigned long rate = clk->rate;
/* If this is the PLL base clock, no more calculations needed */
if (clk->pll_data)
return rate;
if (WARN_ON(!clk->parent))
return rate;
rate = clk->parent->rate;
/* Otherwise, the parent must be a PLL */
if (WARN_ON(!clk->parent->pll_data))
return rate;
/*pll=pll0_clk->pll_data=pll0_data*/
pll = clk->parent->pll_data;
/*rate=pll0_data->input_rate=
/* If pre-PLL, source clock is before the multiplier and divider(s) */
if (clk->flags & PRE_PLL)
rate = pll->input_rate;
if (!clk->div_reg)
return rate;
v = __raw_readl(pll->base + clk->div_reg);
if (v & PLLDIV_EN) {
plldiv = (v & pll->div_ratio_mask) + 1;
if (plldiv)
rate /= plldiv;
}
return rate;
}
执行完之后,返回的rate是24Mhz。
再来分析:
static struct clk pll0_sysclk1 = {
.name = "pll0_sysclk1",
.parent = &pll0_clk,
.flags = CLK_PLL,
.div_reg = PLLDIV1,
};
和上一个类型,调用的函数是:
clk->rate = clk->recalc(clk);
即clk->rate =clk_sysclk_recalc( pll0_sysclk1);
rate = clk->parent->rate=456MHz;
pll = clk->parent->pll_data;
pll= &pll0_data
但是在下一个if语句的判断上:
if (!clk->div_reg)
return rate;
是不满足的,所以不会返回,而是继续执行
pll0_sysclk1.div_reg = PLLDIV1,
v = __raw_readl(pll->base + clk->div_reg);
if (v & PLLDIV_EN) {
plldiv = (v & pll->div_ratio_mask) + 1;
if (plldiv)
rate /= plldiv;
}
return rate;
#define PLLDIV1 0x118
所以实际上v读取到的是PLLC0 Pre-Divider Control Register的值,如果预分频被使能的话,就会进行预分频,预分频值为1,所以最终的rate值为456MHz。
下面再分析一个dsp的CLOCK。
static struct clk dsp_clk = {
.name = "dsp",
.parent = &pll0_sysclk1,
.domain = DAVINCI_GPSC_DSPDOMAIN,
.lpsc = DA8XX_LPSC0_GEM,
.flags = ALWAYS_ENABLED,
};
执行如下的函数:
clk_leafclk_recalc(dsp_clk)
{
if (WARN_ON(!clk->parent))
return clk->rate;
return clk->parent->rate;
}
clk->parent->rate=pll0_sysclk1->rate=456Mhz。
所以DSP的主频也达到了456Mhz.
再来分析一下串口0的时钟:
static struct clk uart0_clk = {
.name = "uart0",
.parent = &pll0_sysclk2,
.lpsc = DA8XX_LPSC0_UART0,
};
clk->parent->rate=pll0_sysclk2->rate
所以要先分析pll0_sysclk2的时钟来源,仿照上面pll0_sysclk1的分析,可以得到它的时钟速率为228Mhz。
最后
以上就是传统煎蛋为你收集整理的Linux启动过程分析(十一)--PLL各个外设时钟频率的设置的全部内容,希望文章能够帮你解决Linux启动过程分析(十一)--PLL各个外设时钟频率的设置所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复